IGNITE-2492 .NET: Peer assembly loading (enabled for Compute) This closes #1937
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/69876116 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/69876116 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/69876116 Branch: refs/heads/ignite-5075-pds Commit: 69876116dd851d7bfbf0d82e6850654cc57d3d0b Parents: 42293fa Author: Pavel Tupitsyn <[email protected]> Authored: Mon May 29 16:48:37 2017 +0300 Committer: Pavel Tupitsyn <[email protected]> Committed: Mon May 29 16:48:37 2017 +0300 ---------------------------------------------------------------------- .../Apache.Ignite.Core.Tests.csproj | 9 + .../Binary/TypeNameParserTest.cs | 8 +- .../Deployment/GetAddressFunc.cs | 35 ++++ .../PeerAssemblyLoadingAllApisTest.cs | 167 ++++++++++++++++ .../Deployment/PeerAssemblyLoadingTest.cs | 191 +++++++++++++++++++ .../PeerAssemblyLoadingVersioningTest.cs | 164 ++++++++++++++++ .../Deployment/ProcessNameFunc.cs | 50 +++++ .../Deployment/ProcessNameTask.cs | 74 +++++++ .../Deployment/peer_assembly_app.config | 35 ++++ .../IgniteConfigurationSerializerTest.cs | 10 +- .../Process/IgniteProcess.cs | 12 +- .../Apache.Ignite.Core.csproj | 9 + .../Deployment/PeerAssemblyLoadingMode.cs | 55 ++++++ .../Apache.Ignite.Core/IgniteConfiguration.cs | 16 ++ .../IgniteConfigurationSection.xsd | 12 ++ .../dotnet/Apache.Ignite.Core/Ignition.cs | 18 +- .../Impl/Binary/BinarizableSerializer.cs | 5 +- .../Binary/BinaryEqualityComparerSerializer.cs | 1 - .../Impl/Binary/BinaryProcessor.cs | 2 +- .../Impl/Binary/BinaryReader.cs | 66 ++++--- .../BinaryReflectiveSerializerInternal.cs | 5 +- .../Impl/Binary/BinarySystemTypeSerializer.cs | 2 +- .../Impl/Binary/BinaryWriter.cs | 24 +++ .../Impl/Binary/BinaryWriterExtensions.cs | 9 +- .../Impl/Binary/IBinarySerializerInternal.cs | 10 +- .../Impl/Binary/Marshaller.cs | 37 +++- .../Impl/Binary/SerializableSerializer.cs | 14 +- .../Impl/Binary/TypeResolver.cs | 6 +- .../Impl/Binary/UserSerializerProxy.cs | 5 +- .../Common/CopyOnWriteConcurrentDictionary.cs | 8 - .../Impl/Common/LoadedAssembliesResolver.cs | 8 +- .../Impl/Compute/Closure/ComputeActionJob.cs | 7 +- .../Impl/Compute/Closure/ComputeFuncJob.cs | 5 +- .../Impl/Compute/ComputeFunc.cs | 5 +- .../Impl/Compute/ComputeImpl.cs | 1 - .../Impl/Compute/ComputeJob.cs | 3 +- .../Impl/Compute/ComputeOutFunc.cs | 3 +- .../Impl/Deployment/AssemblyLoader.cs | 105 ++++++++++ .../Impl/Deployment/AssemblyRequest.cs | 68 +++++++ .../Impl/Deployment/AssemblyRequestResult.cs | 80 ++++++++ .../Impl/Deployment/GetAssemblyFunc.cs | 77 ++++++++ .../Impl/Deployment/PeerAssemblyResolver.cs | 189 ++++++++++++++++++ .../Impl/Deployment/PeerLoadingExtensions.cs | 65 +++++++ .../Impl/Deployment/PeerLoadingObjectHolder.cs | 90 +++++++++ .../PeerLoadingObjectHolderSerializer.cs | 49 +++++ 45 files changed, 1716 insertions(+), 98 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/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 974f858..6d4f34b 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 @@ -75,6 +75,11 @@ <Compile Include="Binary\BinaryNameMapperTest.cs" /> <Compile Include="Binary\BinaryReaderWriterTest.cs" /> <Compile Include="Binary\BinarySelfTestSimpleName.cs" /> + <Compile Include="Deployment\GetAddressFunc.cs" /> + <Compile Include="Deployment\PeerAssemblyLoadingAllApisTest.cs" /> + <Compile Include="Deployment\PeerAssemblyLoadingVersioningTest.cs" /> + <Compile Include="Deployment\ProcessNameFunc.cs" /> + <Compile Include="Deployment\ProcessNameTask.cs" /> <Compile Include="Binary\IO\BinaryStreamsTest.cs" /> <Compile Include="Binary\JavaBinaryInteropTest.cs" /> <Compile Include="Binary\JavaTypeMappingTest.cs" /> @@ -86,6 +91,7 @@ <Compile Include="Binary\Serializable\PrimitivesTest.cs" /> <Compile Include="Binary\Serializable\SqlDmlTest.cs" /> <Compile Include="Binary\TypeNameParserTest.cs" /> + <Compile Include="Deployment\PeerAssemblyLoadingTest.cs" /> <Compile Include="Binary\TypeResolverTest.cs" /> <Compile Include="Cache\AddArgCacheEntryProcessor.cs" /> <Compile Include="Cache\Affinity\AffinityKeyTest.cs" /> @@ -379,6 +385,9 @@ <ItemGroup> <None Include="Apache.Ignite.Core.Tests.nunit" /> <None Include="Apache.Ignite.Core.Tests.snk" /> + <None Include="Deployment\peer_assembly_app.config"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> <None Include="custom_app.config"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/TypeNameParserTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/TypeNameParserTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/TypeNameParserTest.cs index e566a4b..8718d32 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/TypeNameParserTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/TypeNameParserTest.cs @@ -43,6 +43,7 @@ namespace Apache.Ignite.Core.Tests.Binary Assert.AreEqual("x", res.GetName()); Assert.AreEqual(0, res.NameStart); Assert.AreEqual(0, res.NameEnd); + Assert.AreEqual(0, res.FullNameEnd); Assert.AreEqual(-1, res.AssemblyStart); Assert.AreEqual(-1, res.AssemblyEnd); Assert.IsNull(res.Generics); @@ -52,6 +53,7 @@ namespace Apache.Ignite.Core.Tests.Binary Assert.AreEqual(7, res.NameStart); Assert.AreEqual(9, res.NameEnd); + Assert.AreEqual(9, res.FullNameEnd); Assert.IsNull(res.Generics); Assert.AreEqual(-1, res.AssemblyStart); @@ -60,6 +62,7 @@ namespace Apache.Ignite.Core.Tests.Binary Assert.AreEqual(7, res.NameStart); Assert.AreEqual(9, res.NameEnd); + Assert.AreEqual(9, res.FullNameEnd); Assert.IsNull(res.Generics); Assert.AreEqual(12, res.AssemblyStart); @@ -85,7 +88,7 @@ namespace Apache.Ignite.Core.Tests.Binary Assert.AreEqual("List`1", res.GetNameWithNamespace()); Assert.AreEqual("Int", res.Generics.Single().GetName()); Assert.AreEqual("Int", res.Generics.Single().GetNameWithNamespace()); - + // Simple name array. res = TypeNameParser.Parse("List`1[[Byte[]]]"); Assert.AreEqual("List`1", res.GetName()); @@ -224,7 +227,7 @@ namespace Apache.Ignite.Core.Tests.Binary CheckType(typeof(int[,,])); CheckType(typeof(int[][])); CheckType(typeof(int[,,,][,,])); - + CheckType(typeof(List<int>[])); CheckType(typeof(List<int>[,])); CheckType(typeof(List<int>[][])); @@ -266,6 +269,7 @@ namespace Apache.Ignite.Core.Tests.Binary } Assert.AreEqual(type.FullName.Length + 2, res.AssemblyStart); + Assert.AreEqual(type.FullName, res.GetFullName()); } private class Nested http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/GetAddressFunc.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/GetAddressFunc.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/GetAddressFunc.cs new file mode 100644 index 0000000..9d08873 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/GetAddressFunc.cs @@ -0,0 +1,35 @@ +/* + * 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.Deployment +{ + extern alias ExamplesDll; + using Apache.Ignite.Core.Compute; + using Address = ExamplesDll::Apache.Ignite.ExamplesDll.Binary.Address; + + /// <summary> + /// Function that returns an instance of a class from another assembly. + /// </summary> + public class GetAddressFunc : IComputeFunc<int, Address> + { + /** <inheritdoc /> */ + public Address Invoke(int arg) + { + return new Address("addr" + arg, arg); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingAllApisTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingAllApisTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingAllApisTest.cs new file mode 100644 index 0000000..29abda9 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingAllApisTest.cs @@ -0,0 +1,167 @@ +/* + * 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.Deployment +{ + extern alias ExamplesDll; + using System.Linq; + using Apache.Ignite.Core.Tests.Compute; + using Apache.Ignite.Core.Tests.Process; + using Apache.Ignite.Log4Net; + using NUnit.Framework; + using Address = ExamplesDll::Apache.Ignite.ExamplesDll.Binary.Address; + + /// <summary> + /// Tests all APIs that support peer assembly loading. + /// </summary> + [Category(TestUtils.CategoryIntensive)] + public class PeerAssemblyLoadingAllApisTest + { + /// <summary> + /// Tests Compute.Call. + /// </summary> + [Test] + public void TestComputeCall([Values(true, false)] bool async) + { + PeerAssemblyLoadingTest.TestDeployment(remoteCompute => + { + Assert.AreEqual("Apache.Ignite", async + ? remoteCompute.CallAsync(new ProcessNameFunc()).Result + : remoteCompute.Call(new ProcessNameFunc())); + }); + } + + /// <summary> + /// Tests Compute.Call. + /// </summary> + [Test] + public void TestComputeAffinityCall([Values(true, false)] bool async) + { + PeerAssemblyLoadingTest.TestDeployment(ignite => + { + var cache = ignite.GetOrCreateCache<int, int>("myCache"); + + var key = TestUtils.GetPrimaryKey(ignite, cache.Name, ignite.GetCluster().ForRemotes().GetNode()); + + var res = async + ? ignite.GetCompute().AffinityCallAsync(cache.Name, key, new ProcessNameFunc()).Result + : ignite.GetCompute().AffinityCall(cache.Name, key, new ProcessNameFunc()); + + Assert.AreEqual("Apache.Ignite", + res); + }); + } + + /// <summary> + /// Tests Compute.Execute. + /// </summary> + [Test] + public void TestComputeExecute([Values(true, false)] bool async) + { + PeerAssemblyLoadingTest.TestDeployment(remoteCompute => + { + // Argument is from different assembly and should be peer deployed as well. + var taskArg = new Address("1", 2); + + Assert.AreEqual("Apache.Ignite_Address [street=1, zip=2]", async + ? remoteCompute.ExecuteAsync(new ProcessNameTask(), taskArg).Result + : remoteCompute.Execute(new ProcessNameTask(), taskArg)); + }); + } + + /// <summary> + /// Tests Compute.Broadcast(IComputeAction). + /// </summary> + [Test] + public void TestComputeBroadcastAction([Values(true, false)] bool async) + { + PeerAssemblyLoadingTest.TestDeployment(remoteCompute => + { + if (async) + { + remoteCompute.BroadcastAsync(new ComputeAction()).Wait(); + } + else + { + remoteCompute.Broadcast(new ComputeAction()); + } + }); + } + + /// <summary> + /// Tests Compute.Broadcast(IComputeFunc{T}). + /// </summary> + [Test] + public void TestComputeBroadcastOutFunc([Values(true, false)] bool async) + { + PeerAssemblyLoadingTest.TestDeployment(remoteCompute => + { + var results = async + ? remoteCompute.BroadcastAsync(new ProcessNameFunc()).Result + : remoteCompute.Broadcast(new ProcessNameFunc()); + + Assert.AreEqual("Apache.Ignite", results.Single()); + }); + } + + /// <summary> + /// Tests Compute.Broadcast(IComputeFunc{TArg, TRes}). + /// </summary> + [Test] + public void TestComputeBroadcastFunc([Values(true, false)] bool async) + { + PeerAssemblyLoadingTest.TestDeployment(remoteCompute => + { + // Argument requires additional assembly. + var taskArg = new IgniteLog4NetLogger(); + + var results = async + ? remoteCompute.BroadcastAsync(new ProcessNameArgFunc(), taskArg).Result + : remoteCompute.Broadcast(new ProcessNameArgFunc(), taskArg); + + Assert.AreEqual("Apache.IgniteApache.Ignite.Log4Net.IgniteLog4NetLogger", results.Single()); + }); + } + + /// <summary> + /// Tests Compute.Apply. + /// </summary> + [Test] + public void TestComputeApply([Values(true, false)] bool async) + { + PeerAssemblyLoadingTest.TestDeployment(remoteCompute => + { + // Argument is from different assembly and should be peer deployed as well. + var taskArg = new Address("1", 2); + + Assert.AreEqual("Apache.IgniteAddress [street=1, zip=2]", async + ? remoteCompute.ApplyAsync(new ProcessNameArgFunc(), taskArg).Result + : remoteCompute.Apply(new ProcessNameArgFunc(), taskArg)); + }); + } + + /// <summary> + /// Tears down the test. + /// </summary> + [TearDown] + public void TearDown() + { + Ignition.StopAll(true); + IgniteProcess.KillAll(); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs new file mode 100644 index 0000000..c74375d --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs @@ -0,0 +1,191 @@ +/* + * 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.Deployment +{ + extern alias ExamplesDll; + using System; + using System.IO; + using System.Threading; + using Apache.Ignite.Core.Cluster; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Compute; + using Apache.Ignite.Core.Deployment; + using Apache.Ignite.Core.Impl; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Tests.Process; + using Apache.Ignite.Log4Net; + using NUnit.Framework; + using Address = ExamplesDll::Apache.Ignite.ExamplesDll.Binary.Address; + + /// <summary> + /// Tests peer assembly loading feature: + /// when user-defined type is not found within loaded assemblies on remote node, + /// corresponding assembly is sent and loaded automatically. + /// </summary> + public class PeerAssemblyLoadingTest + { + /// <summary> + /// Tests that peer loading does not happen when not enabled in config, and error message is informative. + /// </summary> + [Test] + public void TestDisabledPeerLoading() + { + TestDeployment(remoteCompute => + { + var ex = Assert.Throws<IgniteException>(() => remoteCompute.Call(new ProcessNameFunc())); + + Assert.AreEqual("Compute job has failed on remote node, examine InnerException for details.", + ex.Message); + + Assert.IsNotNull(ex.InnerException); + Assert.AreEqual("Failed to deserialize the job [errType=BinaryObjectException, errMsg=No matching " + + "type found for object", ex.InnerException.Message.Substring(0, 102)); + }, false); + } + + /// <summary> + /// Tests single assembly deployment (basic test). + /// </summary> + [Test] + public void TestSingleAssembly() + { + TestDeployment(remoteCompute => + { + Assert.AreEqual("Apache.Ignite", remoteCompute.Call(new ProcessNameFunc())); + }); + } + + /// <summary> + /// Tests that a type which requires multiple assemblies can be peer deployed. + /// </summary> + [Test] + public void TestMultipleAssemblies() + { + TestDeployment(remoteCompute => + { + // GetAddressFunc requires Tests and Examples assemblies. + var result = remoteCompute.Apply(new GetAddressFunc(), 3); + + Assert.IsNotNull(result); + + Assert.AreEqual(3, result.Zip); + Assert.AreEqual("addr3", result.Street); + }); + } + + /// <summary> + /// Tests that a type which requires multiple assemblies can be peer deployed. + /// </summary> + [Test] + public void TestMultipleAssembliesIndirectDependency() + { + TestDeployment(remoteCompute => + { + // Arg is object, but value is from Examples assembly. + Assert.AreEqual("Apache.IgniteAddress [street=Central, zip=2]", remoteCompute.Call( + new ProcessNameFunc {Arg = new Address("Central", 2)})); + }); + } + + /// <summary> + /// Tests that a type which requires multiple assemblies can be peer deployed. + /// </summary> + [Test] + public void TestMultipleAssembliesIndirectDependencyMultiLevel() + { + TestDeployment(remoteCompute => + { + // Arg is object, value is from Apache.Ignite.Log4Net, and it further depends on log4net. + Assert.AreEqual("Apache.IgniteApache.Ignite.Log4Net.IgniteLog4NetLogger", remoteCompute.Call( + new ProcessNameFunc {Arg = new IgniteLog4NetLogger()})); + }); + } + + /// <summary> + /// Tests the peer deployment. + /// </summary> + public static void TestDeployment(Action<ICompute> test, bool enablePeerDeployment = true) + { + TestDeployment((IClusterGroup remoteCluster) => test(remoteCluster.GetCompute()), enablePeerDeployment); + } + + /// <summary> + /// Tests the peer deployment. + /// </summary> + private static void TestDeployment(Action<IClusterGroup> test, bool enablePeerDeployment = true) + { + TestDeployment(ignite => test(ignite.GetCluster().ForRemotes()), enablePeerDeployment); + } + + /// <summary> + /// Tests the peer deployment. + /// </summary> + public static void TestDeployment(Action<IIgnite> test, bool enablePeerDeployment = true) + { + // Copy Apache.Ignite.exe and Apache.Ignite.Core.dll + // to a separate folder so that it does not locate our assembly automatically. + var folder = IgniteUtils.GetTempDirectoryName(); + foreach (var asm in new[] {typeof(IgniteRunner).Assembly, typeof(Ignition).Assembly}) + { + Assert.IsNotNull(asm.Location); + File.Copy(asm.Location, Path.Combine(folder, Path.GetFileName(asm.Location))); + } + + var exePath = Path.Combine(folder, "Apache.Ignite.exe"); + + // Start separate Ignite process without loading current dll. + // ReSharper disable once AssignNullToNotNullAttribute + var config = Path.Combine(Path.GetDirectoryName(typeof(PeerAssemblyLoadingTest).Assembly.Location), + "Deployment\\peer_assembly_app.config"); + + var proc = IgniteProcess.Start(exePath, IgniteHome.Resolve(null), null, + "-ConfigFileName=" + config, "-ConfigSectionName=igniteConfiguration"); + + Thread.Sleep(300); + Assert.IsFalse(proc.HasExited); + + // Start Ignite and execute computation on remote node. + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + PeerAssemblyLoadingMode = enablePeerDeployment + ? PeerAssemblyLoadingMode.CurrentAppDomain + : PeerAssemblyLoadingMode.Disabled + }; + + using (var ignite = Ignition.Start(cfg)) + { + Assert.IsTrue(ignite.WaitTopology(2)); + + for (var i = 0; i < 10; i++) + { + test(ignite); + } + } + } + + /// <summary> + /// Tears down the test. + /// </summary> + [TearDown] + public void TearDown() + { + Ignition.StopAll(true); + IgniteProcess.KillAll(); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingVersioningTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingVersioningTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingVersioningTest.cs new file mode 100644 index 0000000..a6fe76e --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingVersioningTest.cs @@ -0,0 +1,164 @@ +/* + * 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.Deployment +{ + using System; + using System.CodeDom.Compiler; + using System.Diagnostics; + using System.IO; + using Apache.Ignite.Core.Deployment; + using Apache.Ignite.Core.Discovery.Tcp; + using Apache.Ignite.Core.Discovery.Tcp.Static; + using Apache.Ignite.Core.Impl; + using Apache.Ignite.Core.Tests.Process; + using NUnit.Framework; + + /// <summary> + /// Tests assembly versioning: multiple assemblies with same name but different version should be supported. + /// </summary> + public class PeerAssemblyLoadingVersioningTest + { + private static readonly string TempDir = IgniteUtils.GetTempDirectoryName(); + + /// <summary> + /// Sets up the test. + /// </summary> + [SetUp] + public void SetUp() + { + // Copy referenced assemblies. + foreach (var type in new[] { typeof(Ignition), GetType() }) + { + var loc = type.Assembly.Location; + Assert.IsNotNull(loc); + File.Copy(loc, Path.Combine(TempDir, type.Assembly.GetName().Name + ".dll")); + } + } + + /// <summary> + /// Tears down the test. + /// </summary> + [TearDown] + public void TearDown() + { + Directory.Delete(TempDir, true); + } + + /// <summary> + /// Tests that multiple versions of same assembly can be used on remote nodes. + /// </summary> + [Test] + public void TestMultipleVersionsOfSameAssembly() + { + using (Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + PeerAssemblyLoadingMode = PeerAssemblyLoadingMode.CurrentAppDomain, + IgniteInstanceName = "peerDeployTest", + DiscoverySpi = new TcpDiscoverySpi + { + IpFinder = new TcpDiscoveryStaticIpFinder {Endpoints = new[] {"127.0.0.1:47500..47502"}}, + SocketTimeout = TimeSpan.FromSeconds(0.3) + } + })) + { + RunClientProcess(CompileClientNode(Path.Combine(TempDir, "PeerTest.exe"), "1.0.1.0")); + RunClientProcess(CompileClientNode(Path.Combine(TempDir, "PeerTest.exe"), "1.0.2.0")); + } + } + + /// <summary> + /// Runs the client process. + /// </summary> + private static void RunClientProcess(string exePath) + { + var procStart = new ProcessStartInfo + { + FileName = exePath, + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + }; + + var proc = Process.Start(procStart); + Assert.IsNotNull(proc); + + IgniteProcess.AttachProcessConsoleReader(proc); + + Assert.IsTrue(proc.WaitForExit(30000)); + Assert.AreEqual(0, proc.ExitCode); + + File.Delete(exePath); + } + + /// <summary> + /// Compiles the client node. + /// </summary> + private string CompileClientNode(string exePath, string version) + { + var parameters = new CompilerParameters + { + GenerateExecutable = true, + OutputAssembly = exePath, + ReferencedAssemblies = + { + typeof(Ignition).Assembly.Location, + GetType().Assembly.Location, + "System.dll" + } + }; + + var src = @" +using System; +using System.Reflection; + +using Apache.Ignite.Core; +using Apache.Ignite.Core.Deployment; +using Apache.Ignite.Core.Compute; +using Apache.Ignite.Core.Tests; +using Apache.Ignite.Core.Discovery; +using Apache.Ignite.Core.Discovery.Tcp; +using Apache.Ignite.Core.Discovery.Tcp.Static; + +[assembly: AssemblyVersion(""" + version + @""")] + +class Program +{ + static void Main(string[] args) + { + using (var ignite = Ignition.Start(new IgniteConfiguration(TestUtils.GetTestConfiguration(false)) {ClientMode = true, PeerAssemblyLoadingMode = PeerAssemblyLoadingMode.CurrentAppDomain, + DiscoverySpi = new TcpDiscoverySpi { IpFinder = new TcpDiscoveryStaticIpFinder { Endpoints = new[] { ""127.0.0.1:47500..47502"" } }, SocketTimeout = TimeSpan.FromSeconds(0.3) } +})) + { + var res = ignite.GetCompute().Call(new GridNameFunc()); + if (res != ""peerDeployTest_" + version + @""") throw new Exception(""fail: "" + res); + } + } +} + +public class GridNameFunc : IComputeFunc<string> { public string Invoke() { return Ignition.GetIgnite().Name + ""_"" + GetType().Assembly.GetName().Version; } } +"; + + var results = CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromSource(parameters, src); + + Assert.IsEmpty(results.Errors); + + return exePath; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameFunc.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameFunc.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameFunc.cs new file mode 100644 index 0000000..846e11a --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameFunc.cs @@ -0,0 +1,50 @@ +/* + * 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.Deployment +{ + using Apache.Ignite.Core.Compute; + + /// <summary> + /// Function that returns parent process name. + /// </summary> + public class ProcessNameFunc : IComputeFunc<string> + { + /// <summary> + /// Gets or sets the argument. + /// </summary> + public object Arg { get; set; } + + /** <inheritdoc /> */ + public string Invoke() + { + return System.Diagnostics.Process.GetCurrentProcess().ProcessName + Arg; + } + } + + /// <summary> + /// Function that returns parent process name. + /// </summary> + public class ProcessNameArgFunc : IComputeFunc<object, string> + { + /** <inheritdoc /> */ + public string Invoke(object arg) + { + return System.Diagnostics.Process.GetCurrentProcess().ProcessName + arg; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameTask.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameTask.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameTask.cs new file mode 100644 index 0000000..528ad6f --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/ProcessNameTask.cs @@ -0,0 +1,74 @@ +/* + * 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.Deployment +{ + using System.Collections.Generic; + using System.Linq; + using Apache.Ignite.Core.Cluster; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Compute; + + /// <summary> + /// Task that returns process name. + /// </summary> + public class ProcessNameTask : IComputeTask<object, string, string> + { + /** <inheritdoc /> */ + public IDictionary<IComputeJob<string>, IClusterNode> Map(IList<IClusterNode> subgrid, object arg) + { + return subgrid.ToDictionary(x => (IComputeJob<string>) new ProcessNameJob {Arg = arg}, x => x); + } + + /** <inheritdoc /> */ + public ComputeJobResultPolicy OnResult(IComputeJobResult<string> res, IList<IComputeJobResult<string>> rcvd) + { + return ComputeJobResultPolicy.Reduce; + } + + /** <inheritdoc /> */ + public string Reduce(IList<IComputeJobResult<string>> results) + { + var ex = results.Select(x => x.Exception).FirstOrDefault(x => x != null); + + if (ex != null) + throw new IgniteException("Task failed", ex); + + return results.Select(x => x.Data).FirstOrDefault(); + } + + /// <summary> + /// Job. + /// </summary> + private class ProcessNameJob : IComputeJob<string> + { + public object Arg { get; set; } + + /** <inheritdoc /> */ + public string Execute() + { + return System.Diagnostics.Process.GetCurrentProcess().ProcessName + "_" + Arg; + } + + /** <inheritdoc /> */ + public void Cancel() + { + // No-op. + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/peer_assembly_app.config ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/peer_assembly_app.config b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/peer_assembly_app.config new file mode 100644 index 0000000..4cb1d6c --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/peer_assembly_app.config @@ -0,0 +1,35 @@ +<?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" /> + </configSections> + + <igniteConfiguration xmlns="http://ignite.apache.org/schema/dotnet/IgniteConfigurationSection" + localhost="127.0.0.1" peerAssemblyLoadingMode="CurrentAppDomain"> + <discoverySpi type="TcpDiscoverySpi"> + <ipFinder type="TcpDiscoveryStaticIpFinder"> + <endpoints> + <string>127.0.0.1:47500</string> + </endpoints> + </ipFinder> + </discoverySpi> + </igniteConfiguration> +</configuration> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/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 index 5bbf722..a90eae5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs @@ -41,6 +41,7 @@ namespace Apache.Ignite.Core.Tests using Apache.Ignite.Core.Common; using Apache.Ignite.Core.Communication.Tcp; using Apache.Ignite.Core.DataStructures.Configuration; + using Apache.Ignite.Core.Deployment; using Apache.Ignite.Core.Discovery.Tcp; using Apache.Ignite.Core.Discovery.Tcp.Multicast; using Apache.Ignite.Core.Events; @@ -65,7 +66,7 @@ namespace Apache.Ignite.Core.Tests [Test] public void TestPredefinedXml() { - var xml = @"<igniteConfig workDirectory='c:' JvmMaxMemoryMb='1024' MetricsLogFrequency='0:0:10' isDaemon='true' isLateAffinityAssignment='false' springConfigUrl='c:\myconfig.xml' autoGenerateIgniteInstanceName='true'> + var xml = @"<igniteConfig workDirectory='c:' JvmMaxMemoryMb='1024' MetricsLogFrequency='0:0:10' isDaemon='true' isLateAffinityAssignment='false' springConfigUrl='c:\myconfig.xml' autoGenerateIgniteInstanceName='true' peerAssemblyLoadingMode='CurrentAppDomain'> <localhost>127.1.1.1</localhost> <binaryConfiguration compactFooter='false' keepDeserialized='true'> <nameMapper type='Apache.Ignite.Core.Tests.IgniteConfigurationSerializerTest+NameMapper' bar='testBar' /> @@ -92,7 +93,7 @@ namespace Apache.Ignite.Core.Tests <cacheConfiguration> <cacheConfiguration cacheMode='Replicated' readThrough='true' writeThrough='true' enableStatistics='true' writeBehindCoalescing='false' partitionLossPolicy='ReadWriteAll'> <queryEntities> - <queryEntity keyType='System.Int32' valueType='System.String' tableName='myTable'> + <queryEntity keyType='System.Int32' valueType='System.String' tableName='myTable'> <fields> <queryField name='length' fieldType='System.Int32' isKeyField='true' /> </fields> @@ -273,6 +274,8 @@ namespace Apache.Ignite.Core.Tests Assert.AreEqual(89, memPlc.InitialSize); Assert.AreEqual(98, memPlc.MaxSize); Assert.IsTrue(memPlc.MetricsEnabled); + + Assert.AreEqual(PeerAssemblyLoadingMode.CurrentAppDomain, cfg.PeerAssemblyLoadingMode); } /// <summary> @@ -831,7 +834,8 @@ namespace Apache.Ignite.Core.Tests MetricsEnabled = true } } - } + }, + PeerAssemblyLoadingMode = PeerAssemblyLoadingMode.CurrentAppDomain }; } http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs index 5aec1ac..ee62354 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Process/IgniteProcess.cs @@ -197,12 +197,20 @@ namespace Apache.Ignite.Core.Tests.Process Debug.Assert(proc != null); // 3. Attach output readers to avoid hangs. + AttachProcessConsoleReader(proc, outReader); + + return proc; + } + + /// <summary> + /// Attaches the process console reader. + /// </summary> + public static void AttachProcessConsoleReader(Process proc, IIgniteProcessOutputReader outReader = null) + { outReader = outReader ?? DfltOutReader; Attach(proc, proc.StandardOutput, outReader, false); Attach(proc, proc.StandardError, outReader, true); - - return proc; } /// <summary> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/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 9ce9dd2..b37b685 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -98,6 +98,7 @@ <Compile Include="Cache\Configuration\PartitionLossPolicy.cs" /> <Compile Include="Cache\IMemoryMetrics.cs" /> <Compile Include="Common\ExceptionFactory.cs" /> + <Compile Include="Deployment\PeerAssemblyLoadingMode.cs" /> <Compile Include="Events\IEventStorageSpi.cs" /> <Compile Include="Events\MemoryEventStorageSpi.cs" /> <Compile Include="Events\NoopEventStorageSpi.cs" /> @@ -111,6 +112,14 @@ <Compile Include="Impl\Binary\BinaryArrayEqualityComparer.cs" /> <Compile Include="Impl\Binary\BinaryProcessor.cs" /> <Compile Include="Impl\Binary\BinaryReflectiveSerializerInternal.cs" /> + <Compile Include="Impl\Deployment\AssemblyLoader.cs" /> + <Compile Include="Impl\Deployment\AssemblyRequest.cs" /> + <Compile Include="Impl\Deployment\AssemblyRequestResult.cs" /> + <Compile Include="Impl\Deployment\GetAssemblyFunc.cs" /> + <Compile Include="Impl\Deployment\PeerAssemblyResolver.cs" /> + <Compile Include="Impl\Deployment\PeerLoadingExtensions.cs" /> + <Compile Include="Impl\Deployment\PeerLoadingObjectHolder.cs" /> + <Compile Include="Impl\Deployment\PeerLoadingObjectHolderSerializer.cs" /> <Compile Include="Impl\Binary\IBinarySerializerInternal.cs" /> <Compile Include="Binary\Package-Info.cs" /> <Compile Include="Cache\Affinity\AffinityKey.cs" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Deployment/PeerAssemblyLoadingMode.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Deployment/PeerAssemblyLoadingMode.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Deployment/PeerAssemblyLoadingMode.cs new file mode 100644 index 0000000..7217e2a --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Deployment/PeerAssemblyLoadingMode.cs @@ -0,0 +1,55 @@ +/* + * 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.Deployment +{ + using System; + using Apache.Ignite.Core.Compute; + + /// <summary> + /// Peer assembly loading mode. + /// See <see cref="IgniteConfiguration.PeerAssemblyLoadingMode"/>. + /// </summary> + public enum PeerAssemblyLoadingMode + { + /// <summary> + /// Disabled peer assembly loading. Default mode. + /// </summary> + Disabled, + + /// <summary> + /// Automatically load assemblies from remote nodes into the current <see cref="AppDomain"/>. + /// <para /> + /// .NET does not allow assembly unloading, which means that all peer-loaded assemblies will + /// live as long as the current AppDomain lives. This may cause increased memory usage. + /// <para /> + /// Assemblies are distinguished using their fully qualified name. Multiple versions of the same assembly can + /// be loaded and the correct version will be used (according to Type.AssemblyQualifiedName). + /// So in case when a new version of some type needs to be executed on remote nodes, + /// corresponding assembly version should be bumped up. If assembly is recompiled without version increment, + /// it is considered the same as before and won't be updated. + /// <para /> + /// Assemblies are requested from remote nodes on demand. + /// For example, <see cref="IComputeFunc{TRes}"/> is sent to all nodes + /// via <see cref="ICompute.Broadcast{TRes}"/>. Each node then deserializes the instance and, + /// if containing assembly is not present, requests it from originating node (which did the + /// <see cref="ICompute.Broadcast{TRes}"/> call), if it is alive, or from any other node in cluster. + /// Therefore it is possible that eventually all nodes in cluster will have this assebly loaded. + /// </summary> + CurrentAppDomain + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs index 2a6f5f7..8fab8a4 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs @@ -33,7 +33,9 @@ namespace Apache.Ignite.Core using Apache.Ignite.Core.Common; using Apache.Ignite.Core.Communication; using Apache.Ignite.Core.Communication.Tcp; + using Apache.Ignite.Core.Compute; using Apache.Ignite.Core.DataStructures.Configuration; + using Apache.Ignite.Core.Deployment; using Apache.Ignite.Core.Discovery; using Apache.Ignite.Core.Discovery.Tcp; using Apache.Ignite.Core.Events; @@ -979,5 +981,19 @@ namespace Apache.Ignite.Core /// <see cref="MemoryConfiguration"/> for more details. /// </summary> public MemoryConfiguration MemoryConfiguration { get; set; } + + /// <summary> + /// Gets or sets a value indicating how user assemblies should be loaded on remote nodes. + /// <para /> + /// For example, when executing <see cref="ICompute.Call{TRes}(IComputeFunc{TRes})"/>, + /// the assembly with corresponding <see cref="IComputeFunc{TRes}"/> should be loaded on remote nodes. + /// With this option enabled, Ignite will attempt to send the assembly to remote nodes + /// and load it there automatically. + /// <para /> + /// Default is <see cref="Apache.Ignite.Core.Deployment.PeerAssemblyLoadingMode.Disabled"/>. + /// <para /> + /// Peer loading is enabled for <see cref="ICompute"/> functionality. + /// </summary> + public PeerAssemblyLoadingMode PeerAssemblyLoadingMode { get; set; } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/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 index ad92661..bdfa8db 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd @@ -96,6 +96,13 @@ </xs:restriction> </xs:simpleType> + <xs:simpleType name="peerAssemblyLoadingMode" final="restriction"> + <xs:restriction base="xs:string"> + <xs:enumeration value="Disabled" /> + <xs:enumeration value="CurrentAppDomain" /> + </xs:restriction> + </xs:simpleType> + <xs:element name="igniteConfiguration"> <xs:annotation> <xs:documentation>Ignite configuration root.</xs:documentation> @@ -1411,6 +1418,11 @@ </xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute name="peerAssemblyLoadingMode" type="peerAssemblyLoadingMode"> + <xs:annotation> + <xs:documentation>Indicates whether user assemblies should be loaded on remote nodes automatically.</xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> </xs:schema> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/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 fdddbb7..c5d056d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs @@ -84,7 +84,6 @@ namespace Apache.Ignite.Core [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] static Ignition() { - AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload; } @@ -124,7 +123,7 @@ namespace Apache.Ignite.Core } /// <summary> - /// Reads <see cref="IgniteConfiguration"/> from application configuration + /// Reads <see cref="IgniteConfiguration"/> from application configuration /// <see cref="IgniteConfigurationSection"/> with <see cref="ConfigurationSectionName"/> /// name and starts Ignite. /// </summary> @@ -284,7 +283,7 @@ namespace Apache.Ignite.Core if (_startup.Error != null) { // Wrap in a new exception to preserve original stack trace. - throw new IgniteException("Failed to start Ignite.NET, check inner exception for details", + throw new IgniteException("Failed to start Ignite.NET, check inner exception for details", _startup.Error); } @@ -722,18 +721,7 @@ namespace Apache.Ignite.Core GC.Collect(); } - - /// <summary> - /// Handles the AssemblyResolve event of the CurrentDomain control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="args">The <see cref="ResolveEventArgs"/> instance containing the event data.</param> - /// <returns>Manually resolved assembly, or null.</returns> - private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { - return LoadedAssembliesResolver.Instance.GetAssembly(args.Name); - } - + /// <summary> /// Handles the DomainUnload event of the CurrentDomain control. /// </summary> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs index 2273a93..a211360 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs @@ -17,6 +17,7 @@ namespace Apache.Ignite.Core.Impl.Binary { + using System; using System.Runtime.Serialization; using Apache.Ignite.Core.Binary; @@ -38,9 +39,9 @@ namespace Apache.Ignite.Core.Impl.Binary } /** <inheritdoc /> */ - public T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos) + public T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos, Type typeOverride) { - var obj = (T) FormatterServices.GetUninitializedObject(desc.Type); + var obj = (T) FormatterServices.GetUninitializedObject(typeOverride ?? desc.Type); reader.AddHandle(pos, obj); http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs deleted file mode 100644 index 5f28270..0000000 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryEqualityComparerSerializer.cs +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs index f48bcc0..b8937c9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryProcessor.cs @@ -232,7 +232,7 @@ namespace Apache.Ignite.Core.Impl.Binary } /// <summary> - /// Gets the type by id. + /// Gets the type name by id. /// </summary> /// <param name="id">The identifier.</param> /// <returns>Type or null.</returns> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs index 73a0456..e38085c 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs @@ -522,13 +522,17 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Deserialize object. /// </summary> + /// <param name="typeOverride">The type override. + /// There can be multiple versions of the same type when peer assembly loading is enabled. + /// Only first one is registered in Marshaller. + /// This parameter specifies exact type to be instantiated.</param> /// <returns>Deserialized object.</returns> - public T Deserialize<T>() + public T Deserialize<T>(Type typeOverride = null) { T res; // ReSharper disable once CompareNonConstrainedGenericWithNull - if (!TryDeserialize(out res) && default(T) != null) + if (!TryDeserialize(out res, typeOverride) && default(T) != null) throw new BinaryObjectException(string.Format("Invalid data on deserialization. " + "Expected: '{0}' But was: null", typeof (T))); @@ -538,8 +542,15 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Deserialize object. /// </summary> - /// <returns>Deserialized object.</returns> - public bool TryDeserialize<T>(out T res) + /// <param name="res">Deserialized object.</param> + /// <param name="typeOverride">The type override. + /// There can be multiple versions of the same type when peer assembly loading is enabled. + /// Only first one is registered in Marshaller. + /// This parameter specifies exact type to be instantiated.</param> + /// <returns> + /// Deserialized object. + /// </returns> + public bool TryDeserialize<T>(out T res, Type typeOverride = null) { int pos = Stream.Position; @@ -557,12 +568,12 @@ namespace Apache.Ignite.Core.Impl.Binary return false; case BinaryUtils.HdrHnd: - res = ReadHandleObject<T>(pos); + res = ReadHandleObject<T>(pos, typeOverride); return true; case BinaryUtils.HdrFull: - res = ReadFullObject<T>(pos); + res = ReadFullObject<T>(pos, typeOverride); return true; @@ -583,7 +594,7 @@ namespace Apache.Ignite.Core.Impl.Binary throw new BinaryObjectException("Invalid header on deserialization [pos=" + pos + ", hdr=" + hdr + ']'); } - + /// <summary> /// Gets the flag indicating that there is custom type information in raw region. /// </summary> @@ -657,8 +668,14 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Reads the full object. /// </summary> + /// <param name="pos">The position.</param> + /// <param name="typeOverride">The type override. + /// There can be multiple versions of the same type when peer assembly loading is enabled. + /// Only first one is registered in Marshaller. + /// This parameter specifies exact type to be instantiated.</param> + /// <returns>Resulting object</returns> [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "hashCode")] - private T ReadFullObject<T>(int pos) + private T ReadFullObject<T>(int pos, Type typeOverride) { var hdr = BinaryObjectHeader.Read(Stream, pos); @@ -696,24 +713,17 @@ namespace Apache.Ignite.Core.Impl.Binary { // Find descriptor. var desc = hdr.TypeId == BinaryUtils.TypeUnregistered - ? _marsh.GetDescriptor(Type.GetType(ReadString(), true)) - : _marsh.GetDescriptor(hdr.IsUserType, hdr.TypeId, true); + ? _marsh.GetDescriptor(ReadUnregisteredType(typeOverride)) + : _marsh.GetDescriptor(hdr.IsUserType, hdr.TypeId, true, null, typeOverride); // Instantiate object. if (desc.Type == null) { - if (desc is BinarySurrogateTypeDescriptor) - { - throw new BinaryObjectException(string.Format( - "Unknown type ID: {0}. " + - "This usually indicates missing BinaryConfiguration. " + - "Make sure that all nodes have the same BinaryConfiguration.", hdr.TypeId)); - } - throw new BinaryObjectException(string.Format( "No matching type found for object [typeId={0}, typeName={1}]. " + "This usually indicates that assembly with specified type is not loaded on a node. " + - "When using Apache.Ignite.exe, make sure to load assemblies with -assembly parameter.", + "When using Apache.Ignite.exe, make sure to load assemblies with -assembly parameter. " + + "Alternatively, set IgniteConfiguration.PeerAssemblyLoadingEnabled to true.", desc.TypeId, desc.TypeName)); } @@ -728,7 +738,7 @@ namespace Apache.Ignite.Core.Impl.Binary _frame.Raw = false; // Read object. - var obj = desc.Serializer.ReadBinary<T>(this, desc, pos); + var obj = desc.Serializer.ReadBinary<T>(this, desc, pos, typeOverride); _frame.Struct.UpdateReaderStructure(); @@ -746,6 +756,16 @@ namespace Apache.Ignite.Core.Impl.Binary } /// <summary> + /// Reads the unregistered type. + /// </summary> + private Type ReadUnregisteredType(Type knownType) + { + var typeName = ReadString(); // Must read always. + + return knownType ?? Type.GetType(typeName, true); + } + + /// <summary> /// Sets the current schema. /// </summary> private void SetCurSchema(IBinaryTypeDescriptor desc) @@ -815,7 +835,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Reads the handle object. /// </summary> - private T ReadHandleObject<T>(int pos) + private T ReadHandleObject<T>(int pos, Type typeOverride) { // Get handle position. int hndPos = pos - Stream.ReadInt(); @@ -833,7 +853,7 @@ namespace Apache.Ignite.Core.Impl.Binary // No such handler, i.e. we trying to deserialize inner object before deserializing outer. Stream.Seek(hndPos, SeekOrigin.Begin); - hndObj = Deserialize<T>(); + hndObj = Deserialize<T>(typeOverride); } // Notify builder that we deserialized object on other location. @@ -977,7 +997,7 @@ namespace Apache.Ignite.Core.Impl.Binary return default(T); if (hdr == BinaryUtils.HdrHnd) - return ReadHandleObject<T>(Stream.Position - 1); + return ReadHandleObject<T>(Stream.Position - 1, null); if (expHdr != hdr) throw new BinaryObjectException(string.Format("Invalid header on deserialization. " + http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs index 69f7132..62c19ef 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs @@ -84,13 +84,14 @@ namespace Apache.Ignite.Core.Impl.Binary } /** <inheritdoc /> */ - T IBinarySerializerInternal.ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos) + T IBinarySerializerInternal.ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos, + Type typeOverride) { Debug.Assert(_rActions != null); Debug.Assert(reader != null); Debug.Assert(desc != null); - var obj = FormatterServices.GetUninitializedObject(desc.Type); + var obj = FormatterServices.GetUninitializedObject(typeOverride ?? desc.Type); var ctx = GetStreamingContext(reader); http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs index 1ea1f0b..681f57b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs @@ -48,7 +48,7 @@ namespace Apache.Ignite.Core.Impl.Binary } /** <inheritDoc /> */ - public T1 ReadBinary<T1>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos) + public T1 ReadBinary<T1>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos, Type typeOverride) { return TypeCaster<T1>.Cast(_ctor(reader)); } http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs index a5fed48..da2b371 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs @@ -54,6 +54,9 @@ namespace Apache.Ignite.Core.Impl.Binary /** Whether we are currently detaching an object. */ private bool _detaching; + /** Whether we are directly within peer loading object holder. */ + private bool _isInWrapper; + /** Schema holder. */ private readonly BinaryObjectSchemaHolder _schema = BinaryObjectSchemaHolder.Current; @@ -1165,6 +1168,22 @@ namespace Apache.Ignite.Core.Impl.Binary return; } + // Wrap objects as required. + if (WrapperFunc != null && type != WrapperFunc.Method.ReturnType) + { + if (_isInWrapper) + { + _isInWrapper = false; + } + else + { + _isInWrapper = true; + Write(WrapperFunc(obj)); + + return; + } + } + // Suppose that we faced normal object and perform descriptor lookup. var desc = _marsh.GetDescriptor(type); @@ -1424,6 +1443,11 @@ namespace Apache.Ignite.Core.Impl.Binary } /// <summary> + /// Gets or sets a function to wrap all serializer objects. + /// </summary> + internal Func<object, object> WrapperFunc { get; set; } + + /// <summary> /// Stream. /// </summary> internal IBinaryStream Stream http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriterExtensions.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriterExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriterExtensions.cs index b13a9ea..64bfa35 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriterExtensions.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriterExtensions.cs @@ -18,6 +18,7 @@ namespace Apache.Ignite.Core.Impl.Binary { using System; + using Apache.Ignite.Core.Binary; /// <summary> /// Writer extensions. @@ -27,7 +28,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Writes the nullable boolean. /// </summary> - public static void WriteBooleanNullable(this BinaryWriter writer, bool? value) + public static void WriteBooleanNullable(this IBinaryRawWriter writer, bool? value) { if (value != null) { @@ -41,7 +42,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Writes the nullable boolean. /// </summary> - public static void WriteIntNullable(this BinaryWriter writer, int? value) + public static void WriteIntNullable(this IBinaryRawWriter writer, int? value) { if (value != null) { @@ -55,7 +56,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Writes the timespan. /// </summary> - public static void WriteTimeSpanAsLong(this BinaryWriter writer, TimeSpan value) + public static void WriteTimeSpanAsLong(this IBinaryRawWriter writer, TimeSpan value) { writer.WriteLong((long) value.TotalMilliseconds); } @@ -63,7 +64,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Writes the nullable boolean. /// </summary> - public static void WriteTimeSpanAsLongNullable(this BinaryWriter writer, TimeSpan? value) + public static void WriteTimeSpanAsLongNullable(this IBinaryRawWriter writer, TimeSpan? value) { if (value != null) { http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs index b775999..42145e5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs @@ -17,6 +17,8 @@ namespace Apache.Ignite.Core.Impl.Binary { + using System; + /// <summary> /// Internal generic serializer interface. /// </summary> @@ -25,12 +27,18 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Write binary object. /// </summary> + /// <param name="obj">The object.</param> + /// <param name="writer">The writer.</param> void WriteBinary<T>(T obj, BinaryWriter writer); /// <summary> /// Read binary object. /// </summary> - T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos); + /// <param name="reader">The reader.</param> + /// <param name="desc">The descriptor.</param> + /// <param name="pos">The position.</param> + /// <param name="typeOverride">Type override, can be null.</param> + T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos, Type typeOverride); /// <summary> /// Gets a value indicating whether this serializer supports handles. http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/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 8f12acf..9634e27 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -33,6 +33,7 @@ namespace Apache.Ignite.Core.Impl.Binary using Apache.Ignite.Core.Impl.Compute; using Apache.Ignite.Core.Impl.Compute.Closure; using Apache.Ignite.Core.Impl.Datastream; + using Apache.Ignite.Core.Impl.Deployment; using Apache.Ignite.Core.Impl.Messaging; using Apache.Ignite.Core.Log; @@ -339,7 +340,7 @@ namespace Apache.Ignite.Core.Impl.Binary IDictionary<int, BinaryTypeHolder> metas0 = new Dictionary<int, BinaryTypeHolder>(_metas); - holder = new BinaryTypeHolder(desc.TypeId, desc.TypeName, desc.AffinityKeyFieldName, + holder = new BinaryTypeHolder(desc.TypeId, desc.TypeName, desc.AffinityKeyFieldName, desc.IsEnum, this); metas0[desc.TypeId] = holder; @@ -423,11 +424,12 @@ namespace Apache.Ignite.Core.Impl.Binary /// Only when we really deserialize the value, requiresType is set to true /// and we attempt to resolve the type by all means.</param> /// <param name="typeName">Known type name.</param> + /// <param name="knownType">Optional known type.</param> /// <returns> /// Descriptor. /// </returns> - public IBinaryTypeDescriptor GetDescriptor(bool userType, int typeId, bool requiresType = false, - string typeName = null) + public IBinaryTypeDescriptor GetDescriptor(bool userType, int typeId, bool requiresType = false, + string typeName = null, Type knownType = null) { BinaryFullTypeDescriptor desc; @@ -442,18 +444,29 @@ namespace Apache.Ignite.Core.Impl.Binary if (requiresType && _ignite != null) { // Check marshaller context for dynamically registered type. - typeName = typeName ?? _ignite.BinaryProcessor.GetTypeName(typeId); + var type = knownType; - if (typeName != null) + if (type == null && _ignite != null) { - var type = new TypeResolver().ResolveType(typeName, nameMapper: - _cfg.NameMapper ?? GetDefaultNameMapper()); + typeName = typeName ?? _ignite.BinaryProcessor.GetTypeName(typeId); - if (type != null) + if (typeName != null) { - return AddUserType(type, typeId, GetTypeName(type), true, desc); + type = new TypeResolver().ResolveType(typeName, + nameMapper: _cfg.NameMapper ?? GetDefaultNameMapper()); + + if (type == null) + { + // Type is registered, but assembly is not present. + return new BinarySurrogateTypeDescriptor(_cfg, typeId, typeName); + } } } + + if (type != null) + { + return AddUserType(type, typeId, GetTypeName(type), true, desc); + } } var meta = GetBinaryType(typeId); @@ -593,7 +606,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Gets the serializer. /// </summary> - private static IBinarySerializerInternal GetSerializer(BinaryConfiguration cfg, + private static IBinarySerializerInternal GetSerializer(BinaryConfiguration cfg, BinaryTypeConfiguration typeCfg, Type type, int typeId, IBinaryNameMapper nameMapper, IBinaryIdMapper idMapper, ILogger log) { @@ -732,6 +745,10 @@ namespace Apache.Ignite.Core.Impl.Binary AddSystemType(BinaryUtils.TypePlatformJavaObjectFactoryProxy, r => new PlatformJavaObjectFactoryProxy()); AddSystemType(0, r => new ObjectInfoHolder(r)); AddSystemType(BinaryUtils.TypeIgniteUuid, r => new IgniteGuid(r)); + AddSystemType(0, r => new GetAssemblyFunc()); + AddSystemType(0, r => new AssemblyRequest(r)); + AddSystemType(0, r => new AssemblyRequestResult(r)); + AddSystemType<PeerLoadingObjectHolder>(0, null, serializer: new PeerLoadingObjectHolderSerializer()); } /// <summary> http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs index 1e4af4b..13310e4 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs @@ -87,11 +87,13 @@ namespace Apache.Ignite.Core.Impl.Binary } /** <inheritdoc /> */ - public T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos) + public T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos, Type typeOverride) { object res; var ctx = GetStreamingContext(reader); + var type = typeOverride ?? desc.Type; + // Read additional information from raw part, if flag is set. IEnumerable<string> fieldNames; Type customType = null; @@ -119,7 +121,7 @@ namespace Apache.Ignite.Core.Impl.Binary if (customType != null) { // Custom type is present, which returns original type via IObjectReference. - var serInfo = ReadSerializationInfo(reader, fieldNames, desc, dotNetFields); + var serInfo = ReadSerializationInfo(reader, fieldNames, type, dotNetFields); res = ReadAsCustomType(customType, serInfo, ctx); @@ -131,7 +133,7 @@ namespace Apache.Ignite.Core.Impl.Binary } else { - res = FormatterServices.GetUninitializedObject(desc.Type); + res = FormatterServices.GetUninitializedObject(type); _serializableTypeDesc.OnDeserializing(res, ctx); @@ -140,7 +142,7 @@ namespace Apache.Ignite.Core.Impl.Binary reader.AddHandle(pos, res); // Read actual data and call constructor. - var serInfo = ReadSerializationInfo(reader, fieldNames, desc, dotNetFields); + var serInfo = ReadSerializationInfo(reader, fieldNames, type, dotNetFields); _serializableTypeDesc.SerializationCtorUninitialized(res, serInfo, ctx); } @@ -536,9 +538,9 @@ namespace Apache.Ignite.Core.Impl.Binary /// Reads the serialization information. /// </summary> private static SerializationInfo ReadSerializationInfo(BinaryReader reader, - IEnumerable<string> fieldNames, IBinaryTypeDescriptor desc, ICollection<int> dotNetFields) + IEnumerable<string> fieldNames, Type type, ICollection<int> dotNetFields) { - var serInfo = new SerializationInfo(desc.Type, new FormatterConverter()); + var serInfo = new SerializationInfo(type, new FormatterConverter()); if (dotNetFields == null) { http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/TypeResolver.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/TypeResolver.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/TypeResolver.cs index fa59d62..6fa7918 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/TypeResolver.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/TypeResolver.cs @@ -68,7 +68,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <param name="typeName">Name of the type.</param> /// <param name="assemblies">Assemblies to look in.</param> /// <param name="nameMapper">The name mapper.</param> - /// <returns> + /// <returns> /// Resolved type. /// </returns> private static Type ResolveType(string assemblyName, TypeNameParser typeName, ICollection<Assembly> assemblies, @@ -144,7 +144,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <param name="assemblies">The assemblies.</param> /// <param name="nameMapper">The name mapper.</param> /// <returns>Resolved type, or null.</returns> - private static Type ResolveNonGenericType(string assemblyName, string typeName, + private static Type ResolveNonGenericType(string assemblyName, string typeName, ICollection<Assembly> assemblies, IBinaryNameMapper nameMapper) { // Fully-qualified name can be resolved with system mechanism. @@ -187,7 +187,7 @@ namespace Apache.Ignite.Core.Impl.Binary try { - var result = ResolveType(assemblyName, typeName, GetNotLoadedReferencedAssemblies().ToArray(), + var result = ResolveType(assemblyName, typeName, GetNotLoadedReferencedAssemblies().ToArray(), nameMapper); if (result == null) http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/UserSerializerProxy.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/UserSerializerProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/UserSerializerProxy.cs index b0d393d..95762fd 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/UserSerializerProxy.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/UserSerializerProxy.cs @@ -17,6 +17,7 @@ namespace Apache.Ignite.Core.Impl.Binary { + using System; using System.Diagnostics; using System.Runtime.Serialization; using Apache.Ignite.Core.Binary; @@ -47,9 +48,9 @@ namespace Apache.Ignite.Core.Impl.Binary } /** <inheritdoc /> */ - public T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos) + public T ReadBinary<T>(BinaryReader reader, IBinaryTypeDescriptor desc, int pos, Type typeOverride) { - var obj = FormatterServices.GetUninitializedObject(desc.Type); + var obj = FormatterServices.GetUninitializedObject(typeOverride ?? desc.Type); reader.AddHandle(pos, obj); http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/CopyOnWriteConcurrentDictionary.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/CopyOnWriteConcurrentDictionary.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/CopyOnWriteConcurrentDictionary.cs index 78cb8b6..cd26af7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/CopyOnWriteConcurrentDictionary.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/CopyOnWriteConcurrentDictionary.cs @@ -96,13 +96,5 @@ namespace Apache.Ignite.Core.Impl.Common { return _dict.ContainsKey(key); } - - /// <summary> - /// Gets the values. - /// </summary> - public ICollection<TValue> Values - { - get { return _dict.Values; } - } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/69876116/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/LoadedAssembliesResolver.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/LoadedAssembliesResolver.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/LoadedAssembliesResolver.cs index 9628478..06dffb7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/LoadedAssembliesResolver.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/LoadedAssembliesResolver.cs @@ -19,6 +19,7 @@ namespace Apache.Ignite.Core.Impl.Common { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -29,7 +30,8 @@ namespace Apache.Ignite.Core.Impl.Common internal class LoadedAssembliesResolver { // The lazy singleton instance. - private static readonly Lazy<LoadedAssembliesResolver> LazyInstance = new Lazy<LoadedAssembliesResolver>(); + private static readonly Lazy<LoadedAssembliesResolver> LazyInstance = + new Lazy<LoadedAssembliesResolver>(() => new LoadedAssembliesResolver()); // Assemblies map. private volatile Dictionary<string, Assembly> _map; @@ -37,7 +39,7 @@ namespace Apache.Ignite.Core.Impl.Common /// <summary> /// Initializes a new instance of the <see cref="LoadedAssembliesResolver"/> class. /// </summary> - public LoadedAssembliesResolver() + private LoadedAssembliesResolver() { lock (this) { @@ -89,6 +91,8 @@ namespace Apache.Ignite.Core.Impl.Common [SuppressMessage("ReSharper", "InconsistentlySynchronizedField")] public Assembly GetAssembly(string assemblyName) { + Debug.Assert(!string.IsNullOrWhiteSpace(assemblyName)); + Assembly asm; return _map.TryGetValue(assemblyName, out asm) ? asm : null;
