IGNITE-5356 .NET: Fix runtime dependency loading with peer deployment This closes #2063
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/79d45e34 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/79d45e34 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/79d45e34 Branch: refs/heads/ignite-5075 Commit: 79d45e34335d2286bf382d7a8a49a71661f26f79 Parents: 34409fd Author: Pavel Tupitsyn <[email protected]> Authored: Fri Jun 2 12:59:58 2017 +0300 Committer: Pavel Tupitsyn <[email protected]> Committed: Fri Jun 2 12:59:58 2017 +0300 ---------------------------------------------------------------------- .../Apache.Ignite.Core.Tests.TestDll.csproj | 1 + .../TestExtensions.cs | 35 ++++++++++++++++++++ .../Apache.Ignite.Core.Tests.csproj | 1 + .../Deployment/PeerAssemblyLoadingTest.cs | 13 ++++++++ .../Deployment/RuntimeDependencyFunc.cs | 34 +++++++++++++++++++ .../Impl/Compute/ComputeJobHolder.cs | 6 +++- .../Impl/Deployment/GetAssemblyFunc.cs | 28 ++++++++++++++++ .../Impl/Deployment/PeerAssemblyResolver.cs | 26 ++++++++++++++- .../Apache.Ignite.Core/Impl/ExceptionUtils.cs | 1 + 9 files changed, 143 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/Apache.Ignite.Core.Tests.TestDll.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/Apache.Ignite.Core.Tests.TestDll.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/Apache.Ignite.Core.Tests.TestDll.csproj index db7a4d1..0f9e13b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/Apache.Ignite.Core.Tests.TestDll.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/Apache.Ignite.Core.Tests.TestDll.csproj @@ -42,6 +42,7 @@ <ItemGroup> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="TestClass.cs" /> + <Compile Include="TestExtensions.cs" /> </ItemGroup> <ItemGroup> <None Include="Apache.Ignite.Core.Tests.TestDll.snk" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/TestExtensions.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/TestExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/TestExtensions.cs new file mode 100644 index 0000000..65a1484 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.TestDll/TestExtensions.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.TestDll +{ + using System.Linq; + + /// <summary> + /// Extension methods for tests. + /// </summary> + public static class TestExtensions + { + /// <summary> + /// Reverses the string. + /// </summary> + public static string ReverseString(this string str) + { + return new string(str.Reverse().ToArray()); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/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 d459d2a..12b0b6a 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 @@ -120,6 +120,7 @@ <Compile Include="Collections\ReadOnlyCollectionTest.cs" /> <Compile Include="Collections\ReadOnlyDictionaryTest.cs" /> <Compile Include="Common\IgniteGuidTest.cs" /> + <Compile Include="Deployment\RuntimeDependencyFunc.cs" /> <Compile Include="Log\ConcurrentMemoryTarget.cs" /> <Compile Include="Log\DefaultLoggerTest.cs" /> <Compile Include="Log\Log4NetLoggerTest.cs" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/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 index 27a2d65..8245333 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/PeerAssemblyLoadingTest.cs @@ -118,6 +118,19 @@ namespace Apache.Ignite.Core.Tests.Deployment } /// <summary> + /// Tests the runtime dependency: AssemblyResolve event fires during job execution, + /// not during deserialization. This happens with static classes, for example. + /// </summary> + [Test] + public void TestRuntimeDependency() + { + TestDeployment(remoteCompute => + { + Assert.AreEqual("dcba", remoteCompute.Apply(new RuntimeDependencyFunc(), "abcd")); + }); + } + + /// <summary> /// Tests the peer deployment. /// </summary> public static void TestDeployment(Action<ICompute> test, bool enablePeerDeployment = true) http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/RuntimeDependencyFunc.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/RuntimeDependencyFunc.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/RuntimeDependencyFunc.cs new file mode 100644 index 0000000..5b076a9 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Deployment/RuntimeDependencyFunc.cs @@ -0,0 +1,34 @@ +/* + * 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; + using Apache.Ignite.Core.Tests.TestDll; + + /// <summary> + /// Func with runtime dependency via extension method. + /// </summary> + public class RuntimeDependencyFunc : IComputeFunc<string, string> + { + /** <inheritdoc /> */ + public string Invoke(string arg) + { + return arg.ReverseString(); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Compute/ComputeJobHolder.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Compute/ComputeJobHolder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Compute/ComputeJobHolder.cs index 314814d..120d074 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Compute/ComputeJobHolder.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Compute/ComputeJobHolder.cs @@ -26,6 +26,7 @@ namespace Apache.Ignite.Core.Impl.Compute using Apache.Ignite.Core.Impl.Binary.IO; using Apache.Ignite.Core.Impl.Cluster; using Apache.Ignite.Core.Impl.Compute.Closure; + using Apache.Ignite.Core.Impl.Deployment; using Apache.Ignite.Core.Impl.Memory; using Apache.Ignite.Core.Impl.Resource; @@ -102,7 +103,10 @@ namespace Apache.Ignite.Core.Impl.Compute object res; bool success; - Execute0(cancel, out res, out success); + using (PeerAssemblyResolver.GetInstance(_ignite, Guid.Empty)) + { + Execute0(cancel, out res, out success); + } // 2. Try writing result to the stream. ClusterGroupImpl prj = _ignite.ClusterGroup; http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs index 6d54dbf..8f0c266 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/GetAssemblyFunc.cs @@ -17,7 +17,10 @@ namespace Apache.Ignite.Core.Impl.Deployment { + using System; using System.Diagnostics; + using System.IO; + using System.Reflection; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Common; using Apache.Ignite.Core.Compute; @@ -58,6 +61,7 @@ namespace Apache.Ignite.Core.Impl.Deployment return new AssemblyRequestResult(AssemblyLoader.GetAssemblyBytes(asm), null); } + // Try cached assemblies. var bytes = AssemblyLoader.GetAssemblyBytes(arg.AssemblyName); if (bytes != null) @@ -65,6 +69,30 @@ namespace Apache.Ignite.Core.Impl.Deployment return new AssemblyRequestResult(bytes, null); } + // Assembly may be present but not loaded - attempt to load into main context. + try + { + asm = Assembly.Load(arg.AssemblyName); + + if (asm != null) + { + return new AssemblyRequestResult(AssemblyLoader.GetAssemblyBytes(asm), null); + } + } + catch (FileNotFoundException) + { + return null; + } + catch (FileLoadException ex) + { + return new AssemblyRequestResult(null, string.Format("Failed to load assembly: {0} ({1})", asm, ex)); + } + + catch (BadImageFormatException ex) + { + return new AssemblyRequestResult(null, string.Format("Failed to load assembly: {0} ({1})", asm, ex)); + } + return null; } http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs index 607ca57..b81bbbc 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Deployment/PeerAssemblyResolver.cs @@ -45,6 +45,8 @@ namespace Apache.Ignite.Core.Impl.Deployment _handler = (sender, args) => GetAssembly(ignite, args.Name, originNodeId); + // AssemblyResolve handler is called only when aseembly can't be found via normal lookup, + // so we won't end up loading assemblies that are already present. AppDomain.CurrentDomain.AssemblyResolve += _handler; } @@ -55,6 +57,19 @@ namespace Apache.Ignite.Core.Impl.Deployment } /// <summary> + /// Gets an instance of <see cref="PeerAssemblyResolver"/> when peer loading is enabled; otherwise null. + /// </summary> + public static PeerAssemblyResolver GetInstance(Ignite ignite, Guid originNodeId) + { + if (ignite == null || ignite.Configuration.PeerAssemblyLoadingMode == PeerAssemblyLoadingMode.Disabled) + { + return null; + } + + return new PeerAssemblyResolver(ignite, originNodeId); + } + + /// <summary> /// Gets the assembly from remote nodes. /// </summary> /// <param name="typeName">Assembly-qualified type name.</param> @@ -158,7 +173,10 @@ namespace Apache.Ignite.Core.Impl.Deployment /// </summary> private static IEnumerable<Guid> GetDotNetNodes(IIgnite ignite, Guid originNodeId) { - yield return originNodeId; + if (originNodeId != Guid.Empty) + { + yield return originNodeId; + } foreach (var node in ignite.GetCluster().ForDotNet().ForRemotes().GetNodes()) { @@ -184,6 +202,12 @@ namespace Apache.Ignite.Core.Impl.Deployment // Normal situation: node has left. return null; } + catch (AggregateException aex) + { + // Normal situation: node has left. + aex.Handle(e => e is ClusterGroupEmptyException); + return null; + } } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/79d45e34/modules/platforms/dotnet/Apache.Ignite.Core/Impl/ExceptionUtils.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/ExceptionUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/ExceptionUtils.cs index e2d61d7..dd70f5a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/ExceptionUtils.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/ExceptionUtils.cs @@ -75,6 +75,7 @@ namespace Apache.Ignite.Core.Impl // Cluster exceptions. Exs["org.apache.ignite.cluster.ClusterGroupEmptyException"] = (c, m, e, i) => new ClusterGroupEmptyException(m, e); + Exs["org.apache.ignite.internal.cluster.ClusterGroupEmptyCheckedException"] = (c, m, e, i) => new ClusterGroupEmptyException(m, e); Exs["org.apache.ignite.cluster.ClusterTopologyException"] = (c, m, e, i) => new ClusterTopologyException(m, e); // Compute exceptions.
