IGNITE-7013 .NET: macOS support added
authorPavel Tupitsyn <ptupitsyn@apache.org>
Tue, 28 Nov 2017 15:32:16 +0000 (18:32 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Tue, 28 Nov 2017 15:32:16 +0000 (18:32 +0300)
This closes #3091

19 files changed:
modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Common/TestLogger.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Common/TestUtils.DotNetCore.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Log/CustomLoggerTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/TestUtils.Common.cs
modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs
modules/platforms/dotnet/Apache.Ignite.Core/Ignition.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientSocket.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteManager.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Shell.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/DllLoader.cs [moved from modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/DllLoader.cs with 72% similarity]
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/Jvm.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmDll.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmInitArgs.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmOption.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Os.cs
modules/platforms/dotnet/Apache.Ignite.ndproj

diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Common/TestLogger.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests.DotNetCore/Common/TestLogger.cs
new file mode 100644 (file)
index 0000000..8253fb8
--- /dev/null
@@ -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.DotNetCore.Common
+{
+    using System;
+    using System.Diagnostics;
+    using System.Globalization;
+    using System.IO;
+    using Apache.Ignite.Core.Log;
+
+    /// <summary>
+    /// 'dotnet test' swallows console output. This logger writes to a file to overcome this.
+    /// </summary>
+    internal class TestLogger : ILogger
+    {
+        /** */
+        public static readonly TestLogger Instance = new TestLogger();
+
+        /** */
+        private readonly StreamWriter _file;
+
+        /// <summary>
+        /// Prevents a default instance of the <see cref="TestLogger"/> class from being created.
+        /// </summary>
+        private TestLogger()
+        {
+            var binDir = Path.GetDirectoryName(GetType().Assembly.Location);
+            var file = Path.Combine(binDir, "dotnet-test.log");
+
+            if (File.Exists(file))
+            {
+                File.Delete(file);
+            }
+
+            _file = File.AppendText(file);
+        }
+
+        /** <inheritdoc /> */
+        public void Log(LogLevel level, string message, object[] args, IFormatProvider formatProvider, string category,
+            string nativeErrorInfo, Exception ex)
+        {
+            lock (_file)
+            {
+                var text = args != null
+                    ? string.Format(formatProvider ?? CultureInfo.InvariantCulture, message, args)
+                    : message;
+
+                _file.WriteLine(text);
+                _file.Flush();
+            }
+        }
+
+        /** <inheritdoc /> */
+        public bool IsEnabled(LogLevel level)
+        {
+            return level > LogLevel.Debug;
+        }
+    }
+}
index 0f51593..c0586c3 100644 (file)
 namespace Apache.Ignite.Core.Tests
 {
     using System;
-    using System.Collections.Generic;
+    using System.Diagnostics;
     using System.IO;
     using System.Linq;
+    using Apache.Ignite.Core.Log;
+    using Apache.Ignite.Core.Tests.DotNetCore.Common;
 
     public static partial class TestUtils
     {
-        /** */
-        private static readonly IList<string> JvmOpts =
-            new List<string>
-            {
-                "-Duser.timezone=UTC"
-
-                // Uncomment to debug Java
-                //"-Xdebug",
-                //"-Xnoagent",
-                //"-Djava.compiler=NONE",
-                //"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
-            };
-
         /// <summary>
         /// Gets the default code-based test configuration.
         /// </summary>
         public static IgniteConfiguration GetTestConfiguration(string name = null)
         {
+            TestLogger.Instance.Info("GetTestConfiguration: " + GetTestName());
+
             return new IgniteConfiguration
             {
                 DiscoverySpi = GetStaticDiscovery(),
                 Localhost = "127.0.0.1",
-                JvmOptions = JvmOpts,
-                IgniteInstanceName = name
+                JvmOptions = TestJavaOptions(),
+                IgniteInstanceName = name,
+                Logger = TestLogger.Instance
             };
         }
 
         /// <summary>
+        /// Gets the name of the test.
+        /// </summary>
+        private static string GetTestName()
+        {
+            var st = new StackTrace();
+
+            for (var i = 0; i < st.FrameCount; i++)
+            {
+                var frame = st.GetFrame(i);
+                var method = frame.GetMethod();
+
+                if (method.DeclaringType != typeof(TestUtils) 
+                    && method.DeclaringType != typeof(TestBase))
+                {
+                    return $"{method.DeclaringType.Name}.{method.Name}";
+                }
+            }
+
+            return st.GetFrames().Skip(2).Select(x => x.ToString()).FirstOrDefault() ?? "unknown";
+        }
+
+        /// <summary>
         /// Creates a uniquely named, empty temporary directory on disk and returns the full path of that directory.
         /// </summary>
         /// <returns>The full path of the temporary directory.</returns>
index d965b72..4b9af70 100644 (file)
@@ -81,7 +81,9 @@ namespace Apache.Ignite.Core.Tests.Client
                 {
                     Host = "localhost",
                     Port = 2000,
-                    PortRange = 1
+                    PortRange = 1,
+                    SocketSendBufferSize = 100,
+                    SocketReceiveBufferSize = 50
                 }
             };
 
index e80bd3f..50d2dbf 100644 (file)
@@ -78,13 +78,6 @@ namespace Apache.Ignite.Core.Tests.Log
             {
                 Assert.IsTrue(TestLogger.Entries.Any(x => x.Level == level), "No messages with level " + level);
             }
-
-            // Check IgniteHome and classpath messages.
-            Assert.IsTrue(TestLogger.Entries.Any(x => x.Level == LogLevel.Debug &&
-                                                      x.Message == "Classpath resolved to: {0}"));
-            
-            Assert.IsTrue(TestLogger.Entries.Any(x => x.Level == LogLevel.Debug &&
-                                                      x.Message == "IGNITE_HOME resolved to: {0}"));
         }
 
         /// <summary>
index 4f5f3f4..2430300 100644 (file)
@@ -46,7 +46,6 @@ namespace Apache.Ignite.Core.Tests
         private const int DfltBusywaitSleepInterval = 200;
 
         /** */
-
         private static readonly IList<string> TestJvmOpts = Environment.Is64BitProcess
             ? new List<string>
             {
@@ -230,8 +229,6 @@ namespace Apache.Ignite.Core.Tests
             return false;
         }
 
-
-
         /// <summary>
         /// Waits for condition, polling in busy wait loop.
         /// </summary>
index 6deaa9b..5adb501 100644 (file)
     <Compile Include="Impl\DataRegionMetrics.cs" />
     <Compile Include="Impl\PersistentStore\PersistentStoreMetrics.cs" />
     <Compile Include="Impl\Shell.cs" />
-    <Compile Include="Impl\Unmanaged\DllLoader.cs" />
+    <Compile Include="Impl\Unmanaged\Jni\DllLoader.cs" />
     <Compile Include="Impl\Unmanaged\Jni\AppDomains.cs" />
     <Compile Include="Impl\Unmanaged\Jni\CallbackDelegates.cs" />
     <Compile Include="Impl\Unmanaged\Jni\Callbacks.cs" />
     <Compile Include="Impl\Unmanaged\Jni\EnvDelegates.cs" />
     <Compile Include="Impl\Unmanaged\Jni\Env.cs" />
     <Compile Include="Impl\Unmanaged\Jni\EnvInterface.cs" />
+    <Compile Include="Impl\Unmanaged\Jni\JvmDll.cs" />
+    <Compile Include="Impl\Unmanaged\Jni\JvmInitArgs.cs" />
     <Compile Include="Impl\Unmanaged\Jni\JvmInterface.cs" />
     <Compile Include="Impl\PlatformDisposableTargetAdapter.cs" />
     <Compile Include="Impl\PlatformJniTarget.cs" />
     <Compile Include="Impl\Unmanaged\Jni\GlobalRef.cs" />
+    <Compile Include="Impl\Unmanaged\Jni\JvmOption.cs" />
     <Compile Include="Impl\Unmanaged\Jni\MethodId.cs" />
     <Compile Include="Impl\Unmanaged\Jni\NativeMethod.cs" />
     <Compile Include="Impl\Unmanaged\Jni\JniResult.cs" />
index 29b8519..8c39091 100644 (file)
@@ -847,10 +847,9 @@ namespace Apache.Ignite.Core
         public string SpringConfigUrl { get; set; }
 
         /// <summary>
-        /// Path jvm.dll file. If not set, it's location will be determined
-        /// using JAVA_HOME environment variable.
-        /// If path is neither set nor determined automatically, an exception
-        /// will be thrown.
+        /// Path to jvm.dll (libjvm.so on Linux, libjvm.dylib on Mac) file.
+        /// If not set, it's location will be determined using JAVA_HOME environment variable.
+        /// If path is neither set nor determined automatically, an exception will be thrown.
         /// </summary>
         public string JvmDllPath { get; set; }
 
index 41419ac..46b9ec5 100644 (file)
@@ -227,7 +227,7 @@ namespace Apache.Ignite.Core
                 CheckServerGc(cfg, log);
 
                 // 2. Create context.
-                IgniteUtils.LoadDlls(cfg.JvmDllPath, log);
+                JvmDll.Load(cfg.JvmDllPath, log);
 
                 var cbs = IgniteManager.CreateJvmContext(cfg, log);
                 var env = cbs.Jvm.AttachCurrentThread();
index e565f31..078927b 100644 (file)
@@ -213,11 +213,19 @@ namespace Apache.Ignite.Core.Impl.Client
                 {
                     var socket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
                     {
-                        SendBufferSize = cfg.SocketSendBufferSize,
-                        ReceiveBufferSize = cfg.SocketReceiveBufferSize,
                         NoDelay = cfg.TcpNoDelay
                     };
 
+                    if (cfg.SocketSendBufferSize != IgniteClientConfiguration.DefaultSocketBufferSize)
+                    {
+                        socket.SendBufferSize = cfg.SocketSendBufferSize;
+                    }
+
+                    if (cfg.SocketReceiveBufferSize != IgniteClientConfiguration.DefaultSocketBufferSize)
+                    {
+                        socket.ReceiveBufferSize = cfg.SocketReceiveBufferSize;
+                    }
+
                     socket.Connect(ipEndPoint);
 
                     return socket;
index ce84003..47260a7 100644 (file)
@@ -108,6 +108,14 @@ namespace Apache.Ignite.Core.Impl
         /// <returns>JVM.</returns>
         private static Jvm CreateJvm(IgniteConfiguration cfg, ILogger log)
         {
+            // Do not bother with classpath when JVM exists.
+            var jvm = Jvm.Get(true);
+
+            if (jvm != null)
+            {
+                return jvm;
+            }
+
             var cp = Classpath.CreateClasspath(cfg, log: log);
 
             var jvmOpts = GetMergedJvmOptions(cfg);
index 022a5ed..aff028e 100644 (file)
@@ -19,9 +19,7 @@ namespace Apache.Ignite.Core.Impl
 {
     using System;
     using System.Collections.Generic;
-    using System.Globalization;
     using System.IO;
-    using System.Linq;
     using System.Reflection;
     using System.Runtime.InteropServices;
     using System.Text;
@@ -31,9 +29,6 @@ namespace Apache.Ignite.Core.Impl
     using Apache.Ignite.Core.Impl.Binary;
     using Apache.Ignite.Core.Impl.Cluster;
     using Apache.Ignite.Core.Impl.Common;
-    using Apache.Ignite.Core.Impl.Unmanaged;
-    using Apache.Ignite.Core.Log;
-    using Microsoft.Win32;
     using BinaryReader = Apache.Ignite.Core.Impl.Binary.BinaryReader;
 
     /// <summary>
@@ -41,49 +36,9 @@ namespace Apache.Ignite.Core.Impl
     /// </summary>
     internal static class IgniteUtils
     {
-        /** Environment variable: JAVA_HOME. */
-        private const string EnvJavaHome = "JAVA_HOME";
-
-        /** Lookup paths. */
-        private static readonly string[] JvmDllLookupPaths = Os.IsWindows
-            ? new[]
-            {
-                // JRE paths
-                @"bin\server",
-                @"bin\client",
-
-                // JDK paths
-                @"jre\bin\server",
-                @"jre\bin\client",
-                @"jre\bin\default"
-            }
-            : new[]
-            {
-                // JRE paths
-                "lib/amd64/server",
-                "lib/amd64/client",
-
-                // JDK paths
-                "jre/lib/amd64/server",
-                "jre/lib/amd64/client"
-            };
-
-        /** Registry lookup paths. */
-        private static readonly string[] JreRegistryKeys =
-        {
-            @"Software\JavaSoft\Java Runtime Environment",
-            @"Software\Wow6432Node\JavaSoft\Java Runtime Environment"
-        };
-
-        /** Jvm dll file name. */
-        internal static readonly string FileJvmDll = Os.IsWindows ? "jvm.dll" : "libjvm.so";
-
         /** Prefix for temp directory names. */
         private const string DirIgniteTmp = "Ignite_";
         
-        /** Loaded. */
-        private static bool _loaded;        
-
         /** Thread-local random. */
         [ThreadStatic]
         private static Random _rnd;
@@ -127,25 +82,6 @@ namespace Apache.Ignite.Core.Impl
         }
 
         /// <summary>
-        /// Load JVM DLL if needed.
-        /// </summary>
-        /// <param name="configJvmDllPath">JVM DLL path from config.</param>
-        /// <param name="log">Log.</param>
-        public static void LoadDlls(string configJvmDllPath, ILogger log)
-        {
-            if (_loaded)
-            {
-                log.Debug("JNI dll is already loaded.");
-                return;
-            }
-
-            // 1. Load JNI dll.
-            LoadJvmDll(configJvmDllPath, log);
-
-            _loaded = true;
-        }
-
-        /// <summary>
         /// Create new instance of specified class.
         /// </summary>
         /// <param name="typeName">Class name</param>
@@ -195,185 +131,7 @@ namespace Apache.Ignite.Core.Impl
             }
         }
 
-        /// <summary>
-        /// Loads the JVM DLL.
-        /// </summary>
-        private static void LoadJvmDll(string configJvmDllPath, ILogger log)
-        {
-            var messages = new List<string>();
-            foreach (var dllPath in GetJvmDllPaths(configJvmDllPath))
-            {
-                log.Debug("Trying to load JVM dll from [option={0}, path={1}]...", dllPath.Key, dllPath.Value);
-
-                var errInfo = LoadDll(dllPath.Value, FileJvmDll);
-                if (errInfo == null)
-                {
-                    log.Debug("jvm.dll successfully loaded from [option={0}, path={1}]", dllPath.Key, dllPath.Value);
-                    return;
-                }
-
-                var message = string.Format(CultureInfo.InvariantCulture, "[option={0}, path={1}, error={2}]",
-                                                  dllPath.Key, dllPath.Value, errInfo);
-                messages.Add(message);
-
-                log.Debug("Failed to load jvm.dll: " + message);
-
-                if (dllPath.Value == configJvmDllPath)
-                    break;  // if configJvmDllPath is specified and is invalid - do not try other options
-            }
-
-            if (!messages.Any())  // not loaded and no messages - everything was null
-                messages.Add(string.Format(CultureInfo.InvariantCulture, 
-                    "Please specify IgniteConfiguration.JvmDllPath or {0}.", EnvJavaHome));
-
-            if (messages.Count == 1)
-                throw new IgniteException(string.Format(CultureInfo.InvariantCulture, "Failed to load {0} ({1})", 
-                    FileJvmDll, messages[0]));
-
-            var combinedMessage =
-                messages.Aggregate((x, y) => string.Format(CultureInfo.InvariantCulture, "{0}\n{1}", x, y));
-
-            throw new IgniteException(string.Format(CultureInfo.InvariantCulture, "Failed to load {0}:\n{1}", 
-                FileJvmDll, combinedMessage));
-        }
-
-        /// <summary>
-        /// Try loading DLLs first using file path, then using it's simple name.
-        /// </summary>
-        /// <param name="filePath"></param>
-        /// <param name="simpleName"></param>
-        /// <returns>Null in case of success, error info in case of failure.</returns>
-        private static string LoadDll(string filePath, string simpleName)
-        {
-            string res = null;
-
-            if (filePath != null)
-            {
-                res = DllLoader.Load(filePath);
-
-                if (res == null)
-                {
-                    return null;  // Success.
-                }
-            }
-
-            // Failed to load using file path, fallback to simple name.
-            var res2 = DllLoader.Load(simpleName);
-
-            if (res2 == null)
-            {
-                return null;  // Success.
-            }
-
-            return res;
-        }
-
-        /// <summary>
-        /// Gets the JVM DLL paths in order of lookup priority.
-        /// </summary>
-        private static IEnumerable<KeyValuePair<string, string>> GetJvmDllPaths(string configJvmDllPath)
-        {
-            if (!string.IsNullOrEmpty(configJvmDllPath))
-            {
-                yield return new KeyValuePair<string, string>("IgniteConfiguration.JvmDllPath", configJvmDllPath);
-            }
-
-            var javaHomeDir = Environment.GetEnvironmentVariable(EnvJavaHome);
-
-            if (!string.IsNullOrEmpty(javaHomeDir))
-            {
-                foreach (var path in JvmDllLookupPaths)
-                {
-                    yield return
-                        new KeyValuePair<string, string>(EnvJavaHome, Path.Combine(javaHomeDir, path, FileJvmDll));
-                }
-            }
-
-            foreach (var keyValuePair in GetJvmDllPathsFromRegistry().Concat(GetJvmDllPathsFromSymlink()))
-            {
-                yield return keyValuePair;
-            }
-        }
-
-        /// <summary>
-        /// Gets Jvm dll paths from Windows registry.
-        /// </summary>
-        private static IEnumerable<KeyValuePair<string, string>> GetJvmDllPathsFromRegistry()
-        {
-            if (!Os.IsWindows)
-            {
-                yield break;
-            }
-
-            foreach (var regPath in JreRegistryKeys)
-            {
-                using (var jSubKey = Registry.LocalMachine.OpenSubKey(regPath))
-                {
-                    if (jSubKey == null)
-                        continue;
-
-                    var curVer = jSubKey.GetValue("CurrentVersion") as string;
-
-                    // Current version comes first
-                    var versions = new[] {curVer}.Concat(jSubKey.GetSubKeyNames().Where(x => x != curVer));
 
-                    foreach (var ver in versions.Where(v => !string.IsNullOrEmpty(v)))
-                    {
-                        using (var verKey = jSubKey.OpenSubKey(ver))
-                        {
-                            var dllPath = verKey == null ? null : verKey.GetValue("RuntimeLib") as string;
-
-                            if (dllPath != null)
-                                yield return new KeyValuePair<string, string>(verKey.Name, dllPath);
-                        }
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets the Jvm dll paths from symlink.
-        /// </summary>
-        private static IEnumerable<KeyValuePair<string, string>> GetJvmDllPathsFromSymlink()
-        {
-            if (Os.IsWindows)
-            {
-                yield break;
-            }
-
-            const string javaExec = "/usr/bin/java";
-            if (!File.Exists(javaExec))
-            {
-                yield break;
-            }
-
-            var file = Shell.BashExecute("readlink -f /usr/bin/java");
-            // /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
-
-            var dir = Path.GetDirectoryName(file);
-            // /usr/lib/jvm/java-8-openjdk-amd64/jre/bin
-
-            if (dir == null)
-            {
-                yield break;
-            }
-
-            var libFolder = Path.GetFullPath(Path.Combine(dir, "../lib/"));
-            if (!Directory.Exists(libFolder))
-            {
-                yield break;
-            }
-
-            // Predefined path: /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so
-            yield return new KeyValuePair<string, string>(javaExec,
-                Path.Combine(libFolder, "amd64", "server", FileJvmDll));
-
-            // Last resort - custom paths:
-            foreach (var f in Directory.GetFiles(libFolder, FileJvmDll, SearchOption.AllDirectories))
-            {
-                yield return new KeyValuePair<string, string>(javaExec, f);
-            }
-        }
 
         /// <summary>
         /// Creates a uniquely named, empty temporary directory on disk and returns the full path of that directory.
index e48c8fe..45678f0 100644 (file)
 namespace Apache.Ignite.Core.Impl
 {
     using System.Diagnostics;
+    using System.Diagnostics.CodeAnalysis;
 
     /// <summary>
     /// Shell utils (cmd/bash).
     /// </summary>
+    [ExcludeFromCodeCoverage]
     internal static class Shell
     {
         /// <summary>
  * limitations under the License.
  */
 
-namespace Apache.Ignite.Core.Impl.Unmanaged
+namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
 {
     using System;
+    using System.Collections.Generic;
     using System.ComponentModel;
     using System.Diagnostics.CodeAnalysis;
     using System.Runtime.InteropServices;
@@ -48,35 +49,47 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
         /// <summary>
         /// Loads specified DLL.
         /// </summary>
-        /// <returns>Null when successful; error message otherwise.</returns>
-        public static string Load(string dllPath)
+        /// <returns>Library handle and error message.</returns>
+        public static KeyValuePair<IntPtr, string> Load(string dllPath)
         {
             if (Os.IsWindows)
             {
-                return NativeMethodsWindows.LoadLibrary(dllPath) == IntPtr.Zero 
+                var ptr = NativeMethodsWindows.LoadLibrary(dllPath);
+                return new KeyValuePair<IntPtr, string>(ptr, ptr == IntPtr.Zero
                     ? FormatWin32Error(Marshal.GetLastWin32Error()) ?? "Unknown error"
-                    : null;
+                    : null);
+            }
+
+            if (Os.IsMacOs)
+            {
+                var ptr = NativeMethodsMacOs.dlopen(dllPath, RtldGlobal | RtldLazy);
+                return new KeyValuePair<IntPtr, string>(ptr, ptr == IntPtr.Zero
+                    ? GetErrorText(NativeMethodsMacOs.dlerror())
+                    : null);
             }
 
             if (Os.IsLinux)
             {
                 if (Os.IsMono)
                 {
-                    return NativeMethodsMono.dlopen(dllPath, RtldGlobal | RtldLazy) == IntPtr.Zero
+                    var ptr = NativeMethodsMono.dlopen(dllPath, RtldGlobal | RtldLazy);
+                    return new KeyValuePair<IntPtr, string>(ptr, ptr == IntPtr.Zero
                         ? GetErrorText(NativeMethodsMono.dlerror())
-                        : null;
+                        : null);
                 }
 
                 if (Os.IsNetCore)
                 {
-                    return NativeMethodsCore.dlopen(dllPath, RtldGlobal | RtldLazy) == IntPtr.Zero
+                    var ptr = NativeMethodsCore.dlopen(dllPath, RtldGlobal | RtldLazy);
+                    return new KeyValuePair<IntPtr, string>(ptr, ptr == IntPtr.Zero
                         ? GetErrorText(NativeMethodsCore.dlerror())
-                        : null;
+                        : null);
                 }
 
-                return NativeMethodsLinux.dlopen(dllPath, RtldGlobal | RtldLazy) == IntPtr.Zero
+                var lptr = NativeMethodsLinux.dlopen(dllPath, RtldGlobal | RtldLazy);
+                return new KeyValuePair<IntPtr, string>(lptr, lptr == IntPtr.Zero
                     ? GetErrorText(NativeMethodsLinux.dlerror())
-                    : null;
+                    : null);
             }
 
             throw new InvalidOperationException("Unsupported OS: " + Environment.OSVersion);
@@ -172,5 +185,26 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
                 ThrowOnUnmappableChar = true)]
             internal static extern IntPtr dlerror();
         }
+
+        /// <summary>
+        /// macOs uses "libSystem.dylib".
+        /// </summary>
+        internal static class NativeMethodsMacOs
+        {
+            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
+            [DllImport("libSystem.dylib", CharSet = CharSet.Ansi, BestFitMapping = false,
+                ThrowOnUnmappableChar = true)]
+            internal static extern IntPtr dlopen(string filename, int flags);
+
+            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
+            [DllImport("libSystem.dylib", CharSet = CharSet.Ansi, BestFitMapping = false,
+                ThrowOnUnmappableChar = true)]
+            internal static extern IntPtr dlerror();
+
+            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
+            [DllImport("libSystem.dylib", CharSet = CharSet.Ansi, BestFitMapping = false,
+                ThrowOnUnmappableChar = true)]
+            internal static extern IntPtr dlsym(IntPtr handle, string symbol);
+        }
     }
 }
\ No newline at end of file
index ebff15b..3699751 100644 (file)
@@ -126,11 +126,11 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
         /// <summary>
         /// Gets the JVM.
         /// </summary>
-        public static Jvm Get()
+        public static Jvm Get(bool ignoreMissing = false)
         {
             var res = _instance;
 
-            if (res == null)
+            if (res == null && !ignoreMissing)
             {
                 throw new IgniteException("JVM has not been created.");
             }
@@ -205,7 +205,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
             int existingJvmCount;
 
             // Use existing JVM if present.
-            var res = JniNativeMethods.JNI_GetCreatedJavaVMs(out jvm, 1, out existingJvmCount);
+            var res = JvmDll.Instance.GetCreatedJvms(out jvm, 1, out existingJvmCount);
             if (res != JniResult.Success)
             {
                 throw new IgniteException("JNI_GetCreatedJavaVMs failed: " + res);
@@ -238,7 +238,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
             }
 
             IntPtr env;
-            res = JniNativeMethods.JNI_CreateJavaVM(out jvm, out env, &args);
+            res = JvmDll.Instance.CreateJvm(out jvm, out env, &args);
             if (res != JniResult.Success)
             {
                 throw new IgniteException("JNI_CreateJavaVM failed: " + res);
@@ -255,78 +255,6 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
             del = (T) (object) Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
         }
 
-        /// <summary>
-        /// JavaVMOption.
-        /// </summary>
-        [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")]
-        [StructLayout(LayoutKind.Sequential, Pack = 0)]
-        private struct JvmOption
-        {
-            public IntPtr optionString;
-            private readonly IntPtr extraInfo;
-        }
-
-        /// <summary>
-        /// JavaVMInitArgs.
-        /// </summary>
-        [StructLayout(LayoutKind.Sequential, Pack = 0)]
-        private struct JvmInitArgs
-        {
-            public int version;
-            public int nOptions;
-            public JvmOption* options;
-            private readonly byte ignoreUnrecognized;
-        }
-
-        private static class JniNativeMethods
-        {
-            internal static JniResult JNI_CreateJavaVM(out IntPtr pvm, out IntPtr penv,
-                JvmInitArgs* args)
-            {
-                return Os.IsWindows
-                    ? JniNativeMethodsWindows.JNI_CreateJavaVM(out pvm, out penv, args)
-                    : JniNativeMethodsLinux.JNI_CreateJavaVM(out pvm, out penv, args);
-            }
-
-            internal static JniResult JNI_GetCreatedJavaVMs(out IntPtr pvm, int size, out int size2)
-            {
-                return Os.IsWindows
-                    ? JniNativeMethodsWindows.JNI_GetCreatedJavaVMs(out pvm, size, out size2)
-                    : JniNativeMethodsLinux.JNI_GetCreatedJavaVMs(out pvm, size, out size2);
-            }
-        }
-
-        /// <summary>
-        /// DLL imports.
-        /// </summary>
-        private static class JniNativeMethodsWindows
-        {
-            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
-            [DllImport("jvm.dll", CallingConvention = CallingConvention.StdCall)]
-            internal static extern JniResult JNI_CreateJavaVM(out IntPtr pvm, out IntPtr penv,
-                JvmInitArgs* args);
-
-            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
-            [DllImport("jvm.dll", CallingConvention = CallingConvention.StdCall)]
-            internal static extern JniResult JNI_GetCreatedJavaVMs(out IntPtr pvm, int size,
-                [Out] out int size2);
-        }
-
-        /// <summary>
-        /// DLL imports.
-        /// </summary>
-        private static class JniNativeMethodsLinux
-        {
-            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
-            [DllImport("libjvm.so", CallingConvention = CallingConvention.StdCall)]
-            internal static extern JniResult JNI_CreateJavaVM(out IntPtr pvm, out IntPtr penv,
-                JvmInitArgs* args);
-
-            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
-            [DllImport("libjvm.so", CallingConvention = CallingConvention.StdCall)]
-            internal static extern JniResult JNI_GetCreatedJavaVMs(out IntPtr pvm, int size,
-                [Out] out int size2);
-        }
 
         /// <summary>
         /// Provides access to <see cref="Callbacks"/> instance in the default AppDomain.
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmDll.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmDll.cs
new file mode 100644 (file)
index 0000000..28c85ef
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
+    using System.IO;
+    using System.Linq;
+    using System.Runtime.InteropServices;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Log;
+    using Microsoft.Win32;
+
+    /// <summary>
+    /// Jvm.dll loader (libjvm.so on Linux, libjvm.dylib on macOs).
+    /// </summary>
+    internal class JvmDll
+    {
+        /** Cached instance. */
+        private static JvmDll _instance;
+
+        /** Environment variable: JAVA_HOME. */
+        private const string EnvJavaHome = "JAVA_HOME";
+
+        /** Lookup paths. */
+        private static readonly string[] JvmDllLookupPaths = Os.IsWindows
+            ? new[]
+            {
+                // JRE paths
+                @"bin\server",
+                @"bin\client",
+
+                // JDK paths
+                @"jre\bin\server",
+                @"jre\bin\client",
+                @"jre\bin\default"
+            }
+            : new[]
+            {
+                // JRE paths
+                "lib/server",
+                "lib/client",
+                "lib/amd64/server",
+                "lib/amd64/client",
+
+                // JDK paths
+                "jre/lib/server",
+                "jre/lib/client",
+                "jre/lib/amd64/server",
+                "jre/lib/amd64/client"
+            };
+
+        /** Registry lookup paths. */
+        private static readonly string[] JreRegistryKeys =
+        {
+            @"Software\JavaSoft\Java Runtime Environment",
+            @"Software\Wow6432Node\JavaSoft\Java Runtime Environment"
+        };
+
+        /** Jvm dll file name. */
+        internal static readonly string FileJvmDll = Os.IsWindows
+            ? "jvm.dll"
+            : Os.IsMacOs
+                ? "libjvm.dylib"
+                : "libjvm.so";
+
+        /** */
+        private unsafe delegate JniResult CreateJvmDel(out IntPtr pvm, out IntPtr penv, JvmInitArgs* args);
+
+        /** */
+        private delegate JniResult GetCreatedJvmsDel(out IntPtr pvm, int size, out int size2);
+
+        /** */
+        private readonly CreateJvmDel _createJvm;
+
+        /** */
+        private readonly GetCreatedJvmsDel _getCreatedJvms;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="JvmDll"/> class.
+        /// </summary>
+        private unsafe JvmDll(IntPtr ptr)
+        {
+            if (Os.IsMacOs)
+            {
+                if (ptr == IntPtr.Zero)
+                {
+                    // Retrieve already loaded dll by name.
+                    // This happens in default AppDomain when Ignite starts in another domain.
+                    var res = DllLoader.Load(FileJvmDll);
+                    ptr = res.Key;
+
+                    if (res.Key == IntPtr.Zero)
+                    {
+                        throw new IgniteException(
+                            string.Format("{0} has not been loaded: {1}", FileJvmDll, res.Value));
+                    }
+                }
+
+                // dlopen + DllImport combo does not work on macOs, so we have to call dlsym manually.
+                var createJvmPtr = DllLoader.NativeMethodsMacOs.dlsym(ptr, "JNI_CreateJavaVM");
+                _createJvm = (CreateJvmDel) Marshal.GetDelegateForFunctionPointer(createJvmPtr, typeof(CreateJvmDel));
+
+                var getJvmsPtr = DllLoader.NativeMethodsMacOs.dlsym(ptr, "JNI_GetCreatedJavaVMs");
+                _getCreatedJvms = (GetCreatedJvmsDel) Marshal.GetDelegateForFunctionPointer(getJvmsPtr,
+                    typeof(GetCreatedJvmsDel));
+            }
+            else if (Os.IsWindows)
+            {
+                _createJvm = JniNativeMethodsWindows.JNI_CreateJavaVM;
+                _getCreatedJvms = JniNativeMethodsWindows.JNI_GetCreatedJavaVMs;
+            }
+            else
+            {
+                _createJvm = JniNativeMethodsLinux.JNI_CreateJavaVM;
+                _getCreatedJvms = JniNativeMethodsLinux.JNI_GetCreatedJavaVMs;
+            }
+        }
+
+        /// <summary>
+        /// Gets the instance.
+        /// </summary>
+        public static JvmDll Instance
+        {
+            get { return _instance ?? (_instance = new JvmDll(IntPtr.Zero)); }
+        }
+
+        /// <summary>
+        /// Creates the JVM.
+        /// </summary>
+        public unsafe JniResult CreateJvm(out IntPtr pvm, out IntPtr penv, JvmInitArgs* args)
+        {
+            return _createJvm(out pvm, out penv, args);
+        }
+
+        /// <summary>
+        /// Gets the created JVMS.
+        /// </summary>
+        public JniResult GetCreatedJvms(out IntPtr pvm, int size, out int size2)
+        {
+            return _getCreatedJvms(out pvm, size, out size2);
+        }
+
+        /// <summary>
+        /// Loads the JVM DLL into process memory.
+        /// </summary>
+        public static void Load(string configJvmDllPath, ILogger log)
+        {
+            // Load only once.
+            // Locking is performed by the caller three, omit here.
+            if (_instance != null)
+            {
+                log.Debug("JNI dll is already loaded.");
+                return;
+            }
+
+            var messages = new List<string>();
+            foreach (var dllPath in GetJvmDllPaths(configJvmDllPath))
+            {
+                log.Debug("Trying to load {0} from [option={1}, path={2}]...", FileJvmDll, dllPath.Key, dllPath.Value);
+
+                var res = LoadDll(dllPath.Value, FileJvmDll);
+                if (res.Key != IntPtr.Zero)
+                {
+                    log.Debug("{0} successfully loaded from [option={1}, path={2}]",
+                        FileJvmDll, dllPath.Key, dllPath.Value);
+
+                    _instance = new JvmDll(res.Key);
+
+                    return;
+                }
+
+                var message = string.Format(CultureInfo.InvariantCulture, "[option={0}, path={1}, error={2}]",
+                    dllPath.Key, dllPath.Value, res.Value);
+                messages.Add(message);
+
+                log.Debug("Failed to load {0}:  {1}", FileJvmDll, message);
+
+                if (dllPath.Value == configJvmDllPath)
+                    break; // if configJvmDllPath is specified and is invalid - do not try other options
+            }
+
+            if (!messages.Any()) // not loaded and no messages - everything was null
+            {
+                messages.Add(string.Format(CultureInfo.InvariantCulture,
+                    "Please specify IgniteConfiguration.JvmDllPath or {0}.", EnvJavaHome));
+            }
+
+            if (messages.Count == 1)
+            {
+                throw new IgniteException(string.Format(CultureInfo.InvariantCulture, "Failed to load {0} ({1})",
+                    FileJvmDll, messages[0]));
+            }
+
+            var combinedMessage =
+                messages.Aggregate((x, y) => string.Format(CultureInfo.InvariantCulture, "{0}\n{1}", x, y));
+
+            throw new IgniteException(string.Format(CultureInfo.InvariantCulture, "Failed to load {0}:\n{1}",
+                FileJvmDll, combinedMessage));
+        }
+
+        /// <summary>
+        /// Try loading DLLs first using file path, then using it's simple name.
+        /// </summary>
+        /// <param name="filePath"></param>
+        /// <param name="simpleName"></param>
+        /// <returns>Null in case of success, error info in case of failure.</returns>
+        private static KeyValuePair<IntPtr, string> LoadDll(string filePath, string simpleName)
+        {
+            var res = new KeyValuePair<IntPtr, string>();
+
+            if (filePath != null)
+            {
+                res = DllLoader.Load(filePath);
+
+                if (res.Key != IntPtr.Zero)
+                {
+                    return res; // Success.
+                }
+            }
+
+            // Failed to load using file path, fallback to simple name.
+            var res2 = DllLoader.Load(simpleName);
+
+            if (res2.Key != IntPtr.Zero)
+            {
+                return res2; // Success.
+            }
+
+            return res.Value != null ? res : res2;
+        }
+
+        /// <summary>
+        /// Gets the JVM DLL paths in order of lookup priority.
+        /// </summary>
+        private static IEnumerable<KeyValuePair<string, string>> GetJvmDllPaths(string configJvmDllPath)
+        {
+            if (!string.IsNullOrEmpty(configJvmDllPath))
+            {
+                yield return new KeyValuePair<string, string>("IgniteConfiguration.JvmDllPath", configJvmDllPath);
+            }
+
+            var javaHomeDir = Environment.GetEnvironmentVariable(EnvJavaHome);
+
+            if (!string.IsNullOrEmpty(javaHomeDir))
+            {
+                foreach (var path in JvmDllLookupPaths)
+                {
+                    yield return
+                        new KeyValuePair<string, string>(EnvJavaHome, Path.Combine(javaHomeDir, path, FileJvmDll));
+                }
+            }
+
+            foreach (var keyValuePair in
+                GetJvmDllPathsWindows()
+                    .Concat(GetJvmDllPathsLinux())
+                    .Concat(GetJvmDllPathsMacOs()))
+            {
+                yield return keyValuePair;
+            }
+        }
+
+        /// <summary>
+        /// Gets Jvm dll paths from Windows registry.
+        /// </summary>
+        private static IEnumerable<KeyValuePair<string, string>> GetJvmDllPathsWindows()
+        {
+            if (!Os.IsWindows)
+            {
+                yield break;
+            }
+
+            foreach (var regPath in JreRegistryKeys)
+            {
+                using (var jSubKey = Registry.LocalMachine.OpenSubKey(regPath))
+                {
+                    if (jSubKey == null)
+                        continue;
+
+                    var curVer = jSubKey.GetValue("CurrentVersion") as string;
+
+                    // Current version comes first
+                    var versions = new[] {curVer}.Concat(jSubKey.GetSubKeyNames().Where(x => x != curVer));
+
+                    foreach (var ver in versions.Where(v => !string.IsNullOrEmpty(v)))
+                    {
+                        using (var verKey = jSubKey.OpenSubKey(ver))
+                        {
+                            var dllPath = verKey == null ? null : verKey.GetValue("RuntimeLib") as string;
+
+                            if (dllPath != null)
+                                yield return new KeyValuePair<string, string>(verKey.Name, dllPath);
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets the Jvm dll paths from /usr/bin/java symlink.
+        /// </summary>
+        private static IEnumerable<KeyValuePair<string, string>> GetJvmDllPathsLinux()
+        {
+            if (Os.IsWindows || Os.IsMacOs)
+            {
+                yield break;
+            }
+
+            const string javaExec = "/usr/bin/java";
+            if (!File.Exists(javaExec))
+            {
+                yield break;
+            }
+
+            var file = Shell.BashExecute("readlink -f /usr/bin/java");
+            // /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
+
+            var dir = Path.GetDirectoryName(file);
+            // /usr/lib/jvm/java-8-openjdk-amd64/jre/bin
+
+            if (dir == null)
+            {
+                yield break;
+            }
+
+            var libFolder = Path.GetFullPath(Path.Combine(dir, "../lib/"));
+            if (!Directory.Exists(libFolder))
+            {
+                yield break;
+            }
+
+            // Predefined path: /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so
+            yield return new KeyValuePair<string, string>(javaExec,
+                Path.Combine(libFolder, "amd64", "server", FileJvmDll));
+
+            // Last resort - custom paths:
+            foreach (var f in Directory.GetFiles(libFolder, FileJvmDll, SearchOption.AllDirectories))
+            {
+                yield return new KeyValuePair<string, string>(javaExec, f);
+            }
+        }
+
+        /// <summary>
+        /// Gets the JVM DLL paths on macOs.
+        /// </summary>
+        private static IEnumerable<KeyValuePair<string, string>> GetJvmDllPathsMacOs()
+        {
+            const string jvmDir = "/Library/Java/JavaVirtualMachines";
+
+            if (!Directory.Exists(jvmDir))
+            {
+                yield break;
+            }
+
+            const string subDir = "Contents/Home";
+
+            foreach (var dir in Directory.GetDirectories(jvmDir))
+            {
+                foreach (var path in JvmDllLookupPaths)
+                {
+                    yield return
+                        new KeyValuePair<string, string>(dir, Path.Combine(dir, subDir, path, FileJvmDll));
+                }
+            }
+        }
+
+        /// <summary>
+        /// DLL imports.
+        /// </summary>
+        private static unsafe class JniNativeMethodsWindows
+        {
+            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
+            [DllImport("jvm.dll", CallingConvention = CallingConvention.StdCall)]
+            internal static extern JniResult JNI_CreateJavaVM(out IntPtr pvm, out IntPtr penv, JvmInitArgs* args);
+
+            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
+            [DllImport("jvm.dll", CallingConvention = CallingConvention.StdCall)]
+            internal static extern JniResult JNI_GetCreatedJavaVMs(out IntPtr pvm, int size,
+                [Out] out int size2);
+        }
+
+        /// <summary>
+        /// DLL imports.
+        /// </summary>
+        private static unsafe class JniNativeMethodsLinux
+        {
+            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
+            [DllImport("libjvm.so", CallingConvention = CallingConvention.StdCall)]
+            internal static extern JniResult JNI_CreateJavaVM(out IntPtr pvm, out IntPtr penv, JvmInitArgs* args);
+
+            [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
+            [DllImport("libjvm.so", CallingConvention = CallingConvention.StdCall)]
+            internal static extern JniResult JNI_GetCreatedJavaVMs(out IntPtr pvm, int size,
+                [Out] out int size2);
+        }
+   }
+}
\ No newline at end of file
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmInitArgs.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmInitArgs.cs
new file mode 100644 (file)
index 0000000..0b3c655
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
+{
+    using System.Runtime.InteropServices;
+
+    /// <summary>
+    /// JavaVMInitArgs.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential, Pack = 0)]
+    internal unsafe struct JvmInitArgs
+    {
+        public int version;
+        public int nOptions;
+        public JvmOption* options;
+        private readonly byte ignoreUnrecognized;
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmOption.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmOption.cs
new file mode 100644 (file)
index 0000000..3e239d1
--- /dev/null
@@ -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.Impl.Unmanaged.Jni
+{
+    using System;
+    using System.Diagnostics.CodeAnalysis;
+    using System.Runtime.InteropServices;
+
+    /// <summary>
+    /// JavaVMOption.
+    /// </summary>
+    [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")]
+    [StructLayout(LayoutKind.Sequential, Pack = 0)]
+    internal struct JvmOption
+    {
+        public IntPtr optionString;
+        private readonly IntPtr extraInfo;
+    }
+}
index 22ab447..535aa4c 100644 (file)
@@ -39,6 +39,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
                         || platform == PlatformID.Win32S
                         || platform == PlatformID.Win32Windows;
 
+            IsMacOs = IsLinux && Shell.BashExecute("uname").Contains("Darwin");
             IsMono = Type.GetType("Mono.Runtime") != null;
             IsNetCore = !IsMono;
         }
@@ -62,5 +63,10 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
         /// Linux.
         /// </summary>
         public static bool IsLinux { get; private set; }
+
+        /// <summary>
+        /// MacOs.
+        /// </summary>
+        public static bool IsMacOs { get; private set; }
     }
 }
index aa30019..5ed9e3c 100644 (file)
@@ -2478,7 +2478,7 @@ select new {
       <Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[//<Name>Avoid the Singleton pattern</Name>
 warnif count > 0
 from t in Application.Types
-where !t.IsStatic && !t.IsAbstract && (t.IsClass || t.IsStructure) && t.Name != "Jvm"
+where !t.IsStatic && !t.IsAbstract && (t.IsClass || t.IsStructure) && t.Name != "Jvm" && t.Name != "JvmDll"
 
 // All ctors of a singleton are private
 where t.Constructors.Where(ctor => !ctor.IsPrivate).Count() == 0