| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| namespace Apache.Ignite.Core |
| { |
| using System; |
| using System.Collections.Generic; |
| using System.Configuration; |
| using System.Diagnostics.CodeAnalysis; |
| using System.IO; |
| using System.Linq; |
| using System.Reflection; |
| using System.Runtime; |
| using System.Threading; |
| using Apache.Ignite.Core.Binary; |
| using Apache.Ignite.Core.Cache.Affinity; |
| using Apache.Ignite.Core.Client; |
| using Apache.Ignite.Core.Common; |
| using Apache.Ignite.Core.Impl; |
| using Apache.Ignite.Core.Impl.Binary; |
| using Apache.Ignite.Core.Impl.Binary.IO; |
| using Apache.Ignite.Core.Impl.Cache.Affinity; |
| using Apache.Ignite.Core.Impl.Client; |
| using Apache.Ignite.Core.Impl.Common; |
| using Apache.Ignite.Core.Impl.Handle; |
| using Apache.Ignite.Core.Impl.Log; |
| using Apache.Ignite.Core.Impl.Memory; |
| using Apache.Ignite.Core.Impl.Unmanaged; |
| using Apache.Ignite.Core.Impl.Unmanaged.Jni; |
| using Apache.Ignite.Core.Lifecycle; |
| using Apache.Ignite.Core.Log; |
| using Apache.Ignite.Core.Resource; |
| using BinaryReader = Apache.Ignite.Core.Impl.Binary.BinaryReader; |
| using UU = Apache.Ignite.Core.Impl.Unmanaged.UnmanagedUtils; |
| |
| /// <summary> |
| /// This class defines a factory for the main Ignite API. |
| /// <p/> |
| /// Use <see cref="Start()"/> method to start Ignite with default configuration. |
| /// <para/> |
| /// All members are thread-safe and may be used concurrently from multiple threads. |
| /// </summary> |
| public static class Ignition |
| { |
| /// <summary> |
| /// Default configuration section name. |
| /// </summary> |
| public const string ConfigurationSectionName = "igniteConfiguration"; |
| |
| /// <summary> |
| /// Default configuration section name. |
| /// </summary> |
| public const string ClientConfigurationSectionName = "igniteClientConfiguration"; |
| |
| /// <summary> |
| /// Environment variable name to enable alternate stack checks on .NET Core 3+ and .NET 5+. |
| /// This is required to fix "Stack smashing detected" errors that occur instead of NullReferenceException |
| /// on Linux and macOS when Java overwrites SIGSEGV handler installed by .NET in thick client or server mode. |
| /// </summary> |
| private const string EnvEnableAlternateStackCheck = "COMPlus_EnableAlternateStackCheck"; |
| |
| /** */ |
| private static readonly object SyncRoot = new object(); |
| |
| /** GC warning flag. */ |
| private static int _diagPrinted; |
| |
| /** */ |
| private static readonly IDictionary<NodeKey, Ignite> Nodes = new Dictionary<NodeKey, Ignite>(); |
| |
| /** Current DLL name. */ |
| private static readonly string IgniteDllName = Path.GetFileName(Assembly.GetExecutingAssembly().Location); |
| |
| /** Startup info. */ |
| [ThreadStatic] |
| private static Startup _startup; |
| |
| /** Client mode flag. */ |
| [ThreadStatic] |
| private static bool _clientMode; |
| |
| /// <summary> |
| /// Static initializer. |
| /// </summary> |
| [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] |
| static Ignition() |
| { |
| AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload; |
| } |
| |
| /// <summary> |
| /// Gets or sets a value indicating whether Ignite should be started in client mode. |
| /// Client nodes cannot hold data in caches. |
| /// </summary> |
| public static bool ClientMode |
| { |
| get { return _clientMode; } |
| set { _clientMode = value; } |
| } |
| |
| /// <summary> |
| /// Starts Ignite with default configuration. By default this method will |
| /// use Ignite configuration defined in <c>{IGNITE_HOME}/config/default-config.xml</c> |
| /// configuration file. If such file is not found, then all system defaults will be used. |
| /// </summary> |
| /// <returns>Started Ignite.</returns> |
| public static IIgnite Start() |
| { |
| return Start(new IgniteConfiguration()); |
| } |
| |
| /// <summary> |
| /// Starts all grids specified within given Spring XML configuration file. If Ignite with given name |
| /// is already started, then exception is thrown. In this case all instances that may |
| /// have been started so far will be stopped too. |
| /// </summary> |
| /// <param name="springCfgPath">Spring XML configuration file path or URL. Note, that the path can be |
| /// absolute or relative to IGNITE_HOME.</param> |
| /// <returns>Started Ignite. If Spring configuration contains multiple Ignite instances, then the 1st |
| /// found instance is returned.</returns> |
| public static IIgnite Start(string springCfgPath) |
| { |
| return Start(new IgniteConfiguration {SpringConfigUrl = springCfgPath}); |
| } |
| |
| /// <summary> |
| /// Reads <see cref="IgniteConfiguration"/> from application configuration |
| /// <see cref="IgniteConfigurationSection"/> with <see cref="ConfigurationSectionName"/> |
| /// name and starts Ignite. |
| /// </summary> |
| /// <returns>Started Ignite.</returns> |
| public static IIgnite StartFromApplicationConfiguration() |
| { |
| // ReSharper disable once IntroduceOptionalParameters.Global |
| return StartFromApplicationConfiguration(ConfigurationSectionName); |
| } |
| |
| /// <summary> |
| /// Reads <see cref="IgniteConfiguration"/> from application configuration |
| /// <see cref="IgniteConfigurationSection"/> with specified name and starts Ignite. |
| /// </summary> |
| /// <param name="sectionName">Name of the section.</param> |
| /// <returns>Started Ignite.</returns> |
| public static IIgnite StartFromApplicationConfiguration(string sectionName) |
| { |
| IgniteArgumentCheck.NotNullOrEmpty(sectionName, "sectionName"); |
| |
| var section = ConfigurationManager.GetSection(sectionName) as IgniteConfigurationSection; |
| |
| if (section == null) |
| throw new ConfigurationErrorsException(string.Format("Could not find {0} with name '{1}'", |
| typeof(IgniteConfigurationSection).Name, sectionName)); |
| |
| if (section.IgniteConfiguration == null) |
| throw new ConfigurationErrorsException( |
| string.Format("{0} with name '{1}' is defined in <configSections>, " + |
| "but not present in configuration.", |
| typeof(IgniteConfigurationSection).Name, sectionName)); |
| |
| return Start(section.IgniteConfiguration); |
| } |
| |
| /// <summary> |
| /// Reads <see cref="IgniteConfiguration" /> from application configuration |
| /// <see cref="IgniteConfigurationSection" /> with specified name and starts Ignite. |
| /// </summary> |
| /// <param name="sectionName">Name of the section.</param> |
| /// <param name="configPath">Path to the configuration file.</param> |
| /// <returns>Started Ignite.</returns> |
| public static IIgnite StartFromApplicationConfiguration(string sectionName, string configPath) |
| { |
| var section = GetConfigurationSection<IgniteConfigurationSection>(sectionName, configPath); |
| |
| if (section.IgniteConfiguration == null) |
| { |
| throw new ConfigurationErrorsException( |
| string.Format("{0} with name '{1}' in file '{2}' is defined in <configSections>, " + |
| "but not present in configuration.", |
| typeof(IgniteConfigurationSection).Name, sectionName, configPath)); |
| } |
| |
| return Start(section.IgniteConfiguration); |
| } |
| |
| /// <summary> |
| /// Gets the configuration section. |
| /// </summary> |
| private static T GetConfigurationSection<T>(string sectionName, string configPath) |
| where T : class |
| { |
| IgniteArgumentCheck.NotNullOrEmpty(sectionName, "sectionName"); |
| IgniteArgumentCheck.NotNullOrEmpty(configPath, "configPath"); |
| |
| var fileMap = GetConfigMap(configPath); |
| var config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); |
| |
| var section = config.GetSection(sectionName) as T; |
| |
| if (section == null) |
| { |
| throw new ConfigurationErrorsException( |
| string.Format("Could not find {0} with name '{1}' in file '{2}'", |
| typeof(T).Name, sectionName, configPath)); |
| } |
| |
| return section; |
| } |
| |
| /// <summary> |
| /// Gets the configuration file map. |
| /// </summary> |
| private static ExeConfigurationFileMap GetConfigMap(string fileName) |
| { |
| var fullFileName = Path.GetFullPath(fileName); |
| |
| if (!File.Exists(fullFileName)) |
| throw new ConfigurationErrorsException("Specified config file does not exist: " + fileName); |
| |
| return new ExeConfigurationFileMap { ExeConfigFilename = fullFileName }; |
| } |
| |
| /// <summary> |
| /// Starts Ignite with given configuration. |
| /// </summary> |
| /// <returns>Started Ignite.</returns> |
| public static IIgnite Start(IgniteConfiguration cfg) |
| { |
| IgniteArgumentCheck.NotNull(cfg, "cfg"); |
| |
| cfg = new IgniteConfiguration(cfg); // Create a copy so that config can be modified and reused. |
| |
| lock (SyncRoot) |
| { |
| // 0. Init logger |
| var log = cfg.Logger ?? new JavaLogger(); |
| |
| log.Debug("Starting Ignite.NET " + Assembly.GetExecutingAssembly().GetName().Version); |
| |
| // 1. Log diagnostics. |
| LogDiagnosticMessages(cfg, log); |
| |
| // 2. Create context. |
| JvmDll.Load(cfg.JvmDllPath, log); |
| |
| var cbs = IgniteManager.CreateJvmContext(cfg, log); |
| var env = cbs.Jvm.AttachCurrentThread(); |
| log.Debug("JVM started."); |
| |
| var gridName = cfg.IgniteInstanceName; |
| |
| if (cfg.AutoGenerateIgniteInstanceName) |
| { |
| gridName = (gridName ?? "ignite-instance-") + Guid.NewGuid(); |
| } |
| |
| // 3. Create startup object which will guide us through the rest of the process. |
| _startup = new Startup(cfg, cbs); |
| |
| PlatformJniTarget interopProc = null; |
| |
| try |
| { |
| // 4. Initiate Ignite start. |
| UU.IgnitionStart(env, cfg.SpringConfigUrl, gridName, ClientMode, cfg.Logger != null, cbs.IgniteId, |
| cfg.RedirectJavaConsoleOutput); |
| |
| // 5. At this point start routine is finished. We expect STARTUP object to have all necessary data. |
| var node = _startup.Ignite; |
| interopProc = (PlatformJniTarget)node.InteropProcessor; |
| |
| var javaLogger = log as JavaLogger; |
| if (javaLogger != null) |
| { |
| javaLogger.SetIgnite(node); |
| } |
| |
| // 6. On-start callback (notify lifecycle components). |
| node.OnStart(); |
| |
| Nodes[new NodeKey(_startup.Name)] = node; |
| |
| return node; |
| } |
| catch (Exception ex) |
| { |
| // 1. Perform keys cleanup. |
| string name = _startup.Name; |
| |
| if (name != null) |
| { |
| NodeKey key = new NodeKey(name); |
| |
| if (Nodes.ContainsKey(key)) |
| Nodes.Remove(key); |
| } |
| |
| // 2. Stop Ignite node if it was started. |
| if (interopProc != null) |
| UU.IgnitionStop(gridName, true); |
| |
| // 3. Throw error further (use startup error if exists because it is more precise). |
| 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", |
| _startup.Error); |
| } |
| |
| var jex = ex as JavaException; |
| |
| if (jex == null) |
| { |
| throw; |
| } |
| |
| throw ExceptionUtils.GetException(null, jex); |
| } |
| finally |
| { |
| var ignite = _startup.Ignite; |
| |
| _startup = null; |
| |
| if (ignite != null) |
| { |
| ignite.ProcessorReleaseStart(); |
| } |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Performs system checks and logs diagnostic messages. |
| /// </summary> |
| /// <param name="cfg">Configuration.</param> |
| /// <param name="log">Log.</param> |
| private static void LogDiagnosticMessages(IgniteConfiguration cfg, ILogger log) |
| { |
| if (!cfg.SuppressWarnings && |
| Interlocked.CompareExchange(ref _diagPrinted, 1, 0) == 0) |
| { |
| if (!GCSettings.IsServerGC) |
| { |
| log.Warn("GC server mode is not enabled, this could lead to less " + |
| "than optimal performance on multi-core machines (to enable see " + |
| "https://docs.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector)."); |
| } |
| |
| if ((Os.IsLinux || Os.IsMacOs) && |
| Environment.GetEnvironmentVariable(EnvEnableAlternateStackCheck) != "1") |
| { |
| log.Warn("Alternate stack check is not enabled, this will cause 'Stack smashing detected' " + |
| "error when NullReferenceException occurs on .NET Core on Linux and macOS. " + |
| "To enable alternate stack check on .NET Core 3+ and .NET 5+, " + |
| "set {0} environment variable to 1.", EnvEnableAlternateStackCheck); |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Prepare callback invoked from Java. |
| /// </summary> |
| /// <param name="inStream">Input stream with data.</param> |
| /// <param name="outStream">Output stream.</param> |
| /// <param name="handleRegistry">Handle registry.</param> |
| /// <param name="log">Log.</param> |
| internal static void OnPrepare(PlatformMemoryStream inStream, PlatformMemoryStream outStream, |
| HandleRegistry handleRegistry, ILogger log) |
| { |
| try |
| { |
| BinaryReader reader = BinaryUtils.Marshaller.StartUnmarshal(inStream); |
| |
| PrepareConfiguration(reader, outStream, log); |
| |
| PrepareLifecycleHandlers(reader, outStream, handleRegistry); |
| |
| PrepareAffinityFunctions(reader, outStream); |
| |
| outStream.SynchronizeOutput(); |
| } |
| catch (Exception e) |
| { |
| _startup.Error = e; |
| |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Prepare configuration. |
| /// </summary> |
| /// <param name="reader">Reader.</param> |
| /// <param name="outStream">Response stream.</param> |
| /// <param name="log">Log.</param> |
| private static void PrepareConfiguration(BinaryReader reader, PlatformMemoryStream outStream, ILogger log) |
| { |
| // 1. Load assemblies. |
| IgniteConfiguration cfg = _startup.Configuration; |
| |
| LoadAllAssemblies(cfg.Assemblies); |
| |
| ICollection<string> cfgAssembllies; |
| BinaryConfiguration binaryCfg; |
| |
| BinaryUtils.ReadConfiguration(reader, out cfgAssembllies, out binaryCfg); |
| |
| LoadAllAssemblies(cfgAssembllies); |
| |
| // 2. Create marshaller only after assemblies are loaded. |
| if (cfg.BinaryConfiguration == null) |
| cfg.BinaryConfiguration = binaryCfg; |
| |
| _startup.Marshaller = new Marshaller(cfg.BinaryConfiguration, log); |
| |
| // 3. Send configuration details to Java |
| cfg.Validate(log); |
| // Use system marshaller. |
| cfg.Write(BinaryUtils.Marshaller.StartMarshal(outStream)); |
| } |
| |
| /// <summary> |
| /// Prepare lifecycle handlers. |
| /// </summary> |
| /// <param name="reader">Reader.</param> |
| /// <param name="outStream">Output stream.</param> |
| /// <param name="handleRegistry">Handle registry.</param> |
| private static void PrepareLifecycleHandlers(IBinaryRawReader reader, IBinaryStream outStream, |
| HandleRegistry handleRegistry) |
| { |
| IList<LifecycleHandlerHolder> beans = new List<LifecycleHandlerHolder> |
| { |
| new LifecycleHandlerHolder(new InternalLifecycleHandler()) // add internal bean for events |
| }; |
| |
| // 1. Read beans defined in Java. |
| int cnt = reader.ReadInt(); |
| |
| for (int i = 0; i < cnt; i++) |
| beans.Add(new LifecycleHandlerHolder(CreateObject<ILifecycleHandler>(reader))); |
| |
| // 2. Append beans defined in local configuration. |
| ICollection<ILifecycleHandler> nativeBeans = _startup.Configuration.LifecycleHandlers; |
| |
| if (nativeBeans != null) |
| { |
| foreach (ILifecycleHandler nativeBean in nativeBeans) |
| beans.Add(new LifecycleHandlerHolder(nativeBean)); |
| } |
| |
| // 3. Write bean pointers to Java stream. |
| outStream.WriteInt(beans.Count); |
| |
| foreach (LifecycleHandlerHolder bean in beans) |
| outStream.WriteLong(handleRegistry.AllocateCritical(bean)); |
| |
| // 4. Set beans to STARTUP object. |
| _startup.LifecycleHandlers = beans; |
| } |
| |
| /// <summary> |
| /// Prepares the affinity functions. |
| /// </summary> |
| private static void PrepareAffinityFunctions(BinaryReader reader, PlatformMemoryStream outStream) |
| { |
| var cnt = reader.ReadInt(); |
| |
| var writer = reader.Marshaller.StartMarshal(outStream); |
| |
| for (var i = 0; i < cnt; i++) |
| { |
| var objHolder = new ObjectInfoHolder(reader); |
| AffinityFunctionSerializer.Write(writer, objHolder.CreateInstance<IAffinityFunction>(), objHolder); |
| } |
| } |
| |
| /// <summary> |
| /// Creates an object and sets the properties. |
| /// </summary> |
| /// <param name="reader">Reader.</param> |
| /// <returns>Resulting object.</returns> |
| private static T CreateObject<T>(IBinaryRawReader reader) |
| { |
| return IgniteUtils.CreateInstance<T>(reader.ReadString(), |
| reader.ReadDictionaryAsGeneric<string, object>()); |
| } |
| |
| /// <summary> |
| /// Kernal start callback. |
| /// </summary> |
| /// <param name="interopProc">Interop processor.</param> |
| /// <param name="stream">Stream.</param> |
| [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", |
| Justification = "PlatformJniTarget is passed further")] |
| internal static void OnStart(GlobalRef interopProc, IBinaryStream stream) |
| { |
| try |
| { |
| // 1. Read data and leave critical state ASAP. |
| BinaryReader reader = BinaryUtils.Marshaller.StartUnmarshal(stream); |
| |
| // ReSharper disable once PossibleInvalidOperationException |
| var name = reader.ReadString(); |
| |
| // 2. Set ID and name so that Start() method can use them later. |
| _startup.Name = name; |
| |
| if (Nodes.ContainsKey(new NodeKey(name))) |
| throw new IgniteException("Ignite with the same name already started: " + name); |
| |
| _startup.Ignite = new Ignite(_startup.Configuration, _startup.Name, |
| new PlatformJniTarget(interopProc, _startup.Marshaller), _startup.Marshaller, |
| _startup.LifecycleHandlers, _startup.Callbacks); |
| } |
| catch (Exception e) |
| { |
| // 5. Preserve exception to throw it later in the "Start" method and throw it further |
| // to abort startup in Java. |
| _startup.Error = e; |
| |
| throw; |
| } |
| } |
| |
| /// <summary> |
| /// Load assemblies. |
| /// </summary> |
| /// <param name="assemblies">Assemblies.</param> |
| private static void LoadAllAssemblies(IEnumerable<string> assemblies) |
| { |
| if (assemblies != null) |
| { |
| foreach (var s in assemblies) |
| { |
| LoadAssembly(s); |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Load assembly from file, directory, or full name. |
| /// </summary> |
| /// <param name="asm">Assembly file, directory, or full name.</param> |
| internal static void LoadAssembly(string asm) |
| { |
| // 1. Try loading as directory. |
| if (Directory.Exists(asm)) |
| { |
| string[] files = Directory.GetFiles(asm, "*.dll"); |
| |
| foreach (string dllPath in files) |
| { |
| if (!SelfAssembly(dllPath)) |
| { |
| try |
| { |
| Assembly.LoadFile(dllPath); |
| } |
| |
| catch (BadImageFormatException) |
| { |
| // No-op. |
| } |
| } |
| } |
| |
| return; |
| } |
| |
| // 2. Try loading using full-name. |
| try |
| { |
| Assembly assembly = Assembly.Load(asm); |
| |
| if (assembly != null) |
| return; |
| } |
| catch (Exception e) |
| { |
| if (!(e is FileNotFoundException || e is FileLoadException)) |
| throw new IgniteException("Failed to load assembly: " + asm, e); |
| } |
| |
| // 3. Try loading using file path. |
| try |
| { |
| Assembly assembly = Assembly.LoadFrom(asm); |
| |
| // ReSharper disable once ConditionIsAlwaysTrueOrFalse |
| if (assembly != null) |
| return; |
| } |
| catch (Exception e) |
| { |
| if (!(e is FileNotFoundException || e is FileLoadException)) |
| throw new IgniteException("Failed to load assembly: " + asm, e); |
| } |
| |
| // 4. Not found, exception. |
| throw new IgniteException("Failed to load assembly: " + asm); |
| } |
| |
| /// <summary> |
| /// Whether assembly points to Ignite binary. |
| /// </summary> |
| /// <param name="assembly">Assembly to check..</param> |
| /// <returns><c>True</c> if this is one of GG assemblies.</returns> |
| private static bool SelfAssembly(string assembly) |
| { |
| return assembly.EndsWith(IgniteDllName, StringComparison.OrdinalIgnoreCase); |
| } |
| |
| /// <summary> |
| /// Gets a named Ignite instance. If Ignite name is <c>null</c> or empty string, |
| /// then default no-name Ignite will be returned. Note that caller of this method |
| /// should not assume that it will return the same instance every time. |
| /// <p /> |
| /// Note that single process can run multiple Ignite instances and every Ignite instance (and its |
| /// node) can belong to a different grid. Ignite name defines what grid a particular Ignite |
| /// instance (and correspondingly its node) belongs to. |
| /// </summary> |
| /// <param name="name">Ignite name to which requested Ignite instance belongs. If <c>null</c>, |
| /// then Ignite instance belonging to a default no-name Ignite will be returned.</param> |
| /// <returns> |
| /// An instance of named grid. |
| /// </returns> |
| /// <exception cref="IgniteException">When there is no Ignite instance with specified name.</exception> |
| public static IIgnite GetIgnite(string name) |
| { |
| var ignite = TryGetIgnite(name); |
| |
| if (ignite == null) |
| throw new IgniteException("Ignite instance was not properly started or was already stopped: " + name); |
| |
| return ignite; |
| } |
| |
| /// <summary> |
| /// Gets the default Ignite instance with null name, or an instance with any name when there is only one. |
| /// <para /> |
| /// Note that caller of this method should not assume that it will return the same instance every time. |
| /// </summary> |
| /// <returns>Default Ignite instance.</returns> |
| /// <exception cref="IgniteException">When there is no matching Ignite instance.</exception> |
| public static IIgnite GetIgnite() |
| { |
| lock (SyncRoot) |
| { |
| if (Nodes.Count == 0) |
| { |
| throw new IgniteException("Failed to get default Ignite instance: " + |
| "there are no instances started."); |
| } |
| |
| if (Nodes.Count == 1) |
| { |
| return Nodes.Single().Value; |
| } |
| |
| Ignite result; |
| |
| if (Nodes.TryGetValue(new NodeKey(null), out result)) |
| { |
| return result; |
| } |
| |
| throw new IgniteException(string.Format("Failed to get default Ignite instance: " + |
| "there are {0} instances started, and none of them has null name.", Nodes.Count)); |
| } |
| } |
| |
| /// <summary> |
| /// Gets all started Ignite instances. |
| /// </summary> |
| /// <returns>All Ignite instances.</returns> |
| public static ICollection<IIgnite> GetAll() |
| { |
| lock (SyncRoot) |
| { |
| return Nodes.Values.ToArray(); |
| } |
| } |
| |
| /// <summary> |
| /// Gets a named Ignite instance, or <c>null</c> if none found. If Ignite name is <c>null</c> or empty string, |
| /// then default no-name Ignite will be returned. Note that caller of this method |
| /// should not assume that it will return the same instance every time. |
| /// <p/> |
| /// Note that single process can run multiple Ignite instances and every Ignite instance (and its |
| /// node) can belong to a different grid. Ignite name defines what grid a particular Ignite |
| /// instance (and correspondingly its node) belongs to. |
| /// </summary> |
| /// <param name="name">Ignite name to which requested Ignite instance belongs. If <c>null</c>, |
| /// then Ignite instance belonging to a default no-name Ignite will be returned. |
| /// </param> |
| /// <returns>An instance of named grid, or null.</returns> |
| public static IIgnite TryGetIgnite(string name) |
| { |
| lock (SyncRoot) |
| { |
| Ignite result; |
| |
| return !Nodes.TryGetValue(new NodeKey(name), out result) ? null : result; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the default Ignite instance with null name, or an instance with any name when there is only one. |
| /// Returns null when there are no Ignite instances started, or when there are more than one, |
| /// and none of them has null name. |
| /// </summary> |
| /// <returns>An instance of default no-name grid, or null.</returns> |
| public static IIgnite TryGetIgnite() |
| { |
| lock (SyncRoot) |
| { |
| if (Nodes.Count == 1) |
| { |
| return Nodes.Single().Value; |
| } |
| |
| return TryGetIgnite(null); |
| } |
| } |
| |
| /// <summary> |
| /// Stops named grid. If <c>cancel</c> flag is set to <c>true</c> then |
| /// all jobs currently executing on local node will be interrupted. If |
| /// grid name is <c>null</c>, then default no-name Ignite will be stopped. |
| /// </summary> |
| /// <param name="name">Grid name. If <c>null</c>, then default no-name Ignite will be stopped.</param> |
| /// <param name="cancel">If <c>true</c> then all jobs currently executing will be cancelled |
| /// by calling <c>ComputeJob.cancel</c>method.</param> |
| /// <returns><c>true</c> if named Ignite instance was indeed found and stopped, <c>false</c> |
| /// othwerwise (the instance with given <c>name</c> was not found).</returns> |
| public static bool Stop(string name, bool cancel) |
| { |
| lock (SyncRoot) |
| { |
| NodeKey key = new NodeKey(name); |
| |
| Ignite node; |
| |
| if (!Nodes.TryGetValue(key, out node)) |
| return false; |
| |
| node.Stop(cancel); |
| |
| Nodes.Remove(key); |
| |
| GC.Collect(); |
| |
| return true; |
| } |
| } |
| |
| /// <summary> |
| /// Stops <b>all</b> started grids. If <c>cancel</c> flag is set to <c>true</c> then |
| /// all jobs currently executing on local node will be interrupted. |
| /// </summary> |
| /// <param name="cancel">If <c>true</c> then all jobs currently executing will be cancelled |
| /// by calling <c>ComputeJob.Cancel()</c> method.</param> |
| public static void StopAll(bool cancel) |
| { |
| lock (SyncRoot) |
| { |
| while (Nodes.Count > 0) |
| { |
| var entry = Nodes.First(); |
| |
| entry.Value.Stop(cancel); |
| |
| Nodes.Remove(entry.Key); |
| } |
| } |
| |
| GC.Collect(); |
| } |
| |
| /// <summary> |
| /// Connects Ignite lightweight (thin) client to an Ignite node. |
| /// <para /> |
| /// Thin client connects to an existing Ignite node with a socket and does not start JVM in process. |
| /// </summary> |
| /// <param name="clientConfiguration">The client configuration.</param> |
| /// <returns>Ignite client instance.</returns> |
| public static IIgniteClient StartClient(IgniteClientConfiguration clientConfiguration) |
| { |
| IgniteArgumentCheck.NotNull(clientConfiguration, "clientConfiguration"); |
| |
| return new IgniteClient(clientConfiguration); |
| } |
| |
| /// <summary> |
| /// Reads <see cref="IgniteClientConfiguration"/> from application configuration |
| /// <see cref="IgniteClientConfigurationSection"/> with <see cref="ClientConfigurationSectionName"/> |
| /// name and connects Ignite lightweight (thin) client to an Ignite node. |
| /// <para /> |
| /// Thin client connects to an existing Ignite node with a socket and does not start JVM in process. |
| /// </summary> |
| /// <returns>Ignite client instance.</returns> |
| public static IIgniteClient StartClient() |
| { |
| // ReSharper disable once IntroduceOptionalParameters.Global |
| return StartClient(ClientConfigurationSectionName); |
| } |
| |
| /// <summary> |
| /// Reads <see cref="IgniteClientConfiguration" /> from application configuration |
| /// <see cref="IgniteClientConfigurationSection" /> with specified name and connects |
| /// Ignite lightweight (thin) client to an Ignite node. |
| /// <para /> |
| /// Thin client connects to an existing Ignite node with a socket and does not start JVM in process. |
| /// </summary> |
| /// <param name="sectionName">Name of the configuration section.</param> |
| /// <returns>Ignite client instance.</returns> |
| public static IIgniteClient StartClient(string sectionName) |
| { |
| IgniteArgumentCheck.NotNullOrEmpty(sectionName, "sectionName"); |
| |
| var section = ConfigurationManager.GetSection(sectionName) as IgniteClientConfigurationSection; |
| |
| if (section == null) |
| { |
| throw new ConfigurationErrorsException(string.Format("Could not find {0} with name '{1}'.", |
| typeof(IgniteClientConfigurationSection).Name, sectionName)); |
| } |
| |
| if (section.IgniteClientConfiguration == null) |
| { |
| throw new ConfigurationErrorsException( |
| string.Format("{0} with name '{1}' is defined in <configSections>, " + |
| "but not present in configuration.", |
| typeof(IgniteClientConfigurationSection).Name, sectionName)); |
| } |
| |
| return StartClient(section.IgniteClientConfiguration); |
| } |
| |
| /// <summary> |
| /// Reads <see cref="IgniteConfiguration" /> from application configuration |
| /// <see cref="IgniteConfigurationSection" /> with specified name and starts Ignite. |
| /// </summary> |
| /// <param name="sectionName">Name of the section.</param> |
| /// <param name="configPath">Path to the configuration file.</param> |
| /// <returns>Started Ignite.</returns> |
| public static IIgniteClient StartClient(string sectionName, string configPath) |
| { |
| var section = GetConfigurationSection<IgniteClientConfigurationSection>(sectionName, configPath); |
| |
| if (section.IgniteClientConfiguration == null) |
| { |
| throw new ConfigurationErrorsException( |
| string.Format("{0} with name '{1}' in file '{2}' is defined in <configSections>, " + |
| "but not present in configuration.", |
| typeof(IgniteClientConfigurationSection).Name, sectionName, configPath)); |
| } |
| |
| return StartClient(section.IgniteClientConfiguration); |
| } |
| |
| /// <summary> |
| /// Handles the DomainUnload event of the CurrentDomain control. |
| /// </summary> |
| /// <param name="sender">The source of the event.</param> |
| /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> |
| private static void CurrentDomain_DomainUnload(object sender, EventArgs e) |
| { |
| // If we don't stop Ignite.NET on domain unload, |
| // we end up with broken instances in Java (invalid callbacks, etc). |
| // IIS, in particular, is known to unload and reload domains within the same process. |
| StopAll(true); |
| } |
| |
| /// <summary> |
| /// Grid key. Workaround for non-null key requirement in Dictionary. |
| /// </summary> |
| private class NodeKey |
| { |
| /** */ |
| private readonly string _name; |
| |
| /// <summary> |
| /// Initializes a new instance of the <see cref="NodeKey"/> class. |
| /// </summary> |
| /// <param name="name">The name.</param> |
| internal NodeKey(string name) |
| { |
| _name = name; |
| } |
| |
| /** <inheritdoc /> */ |
| public override bool Equals(object obj) |
| { |
| var other = obj as NodeKey; |
| |
| return other != null && Equals(_name, other._name); |
| } |
| |
| /** <inheritdoc /> */ |
| [SuppressMessage("Microsoft.Globalization", "CA1307:SpecifyStringComparison", Justification = "Not available on .NET FW")] |
| public override int GetHashCode() |
| { |
| return _name == null ? 0 : _name.GetHashCode(); |
| } |
| } |
| |
| /// <summary> |
| /// Value object to pass data between .NET methods during startup bypassing Java. |
| /// </summary> |
| private class Startup |
| { |
| /// <summary> |
| /// Constructor. |
| /// </summary> |
| /// <param name="cfg">Configuration.</param> |
| /// <param name="cbs"></param> |
| internal Startup(IgniteConfiguration cfg, UnmanagedCallbacks cbs) |
| { |
| Configuration = cfg; |
| Callbacks = cbs; |
| } |
| /// <summary> |
| /// Configuration. |
| /// </summary> |
| internal IgniteConfiguration Configuration { get; private set; } |
| |
| /// <summary> |
| /// Gets unmanaged callbacks. |
| /// </summary> |
| internal UnmanagedCallbacks Callbacks { get; private set; } |
| |
| /// <summary> |
| /// Lifecycle handlers. |
| /// </summary> |
| internal IList<LifecycleHandlerHolder> LifecycleHandlers { get; set; } |
| |
| /// <summary> |
| /// Node name. |
| /// </summary> |
| internal string Name { get; set; } |
| |
| /// <summary> |
| /// Marshaller. |
| /// </summary> |
| internal Marshaller Marshaller { get; set; } |
| |
| /// <summary> |
| /// Start error. |
| /// </summary> |
| internal Exception Error { get; set; } |
| |
| /// <summary> |
| /// Gets or sets the ignite. |
| /// </summary> |
| internal Ignite Ignite { get; set; } |
| } |
| |
| /// <summary> |
| /// Internal handler for event notification. |
| /// </summary> |
| private class InternalLifecycleHandler : ILifecycleHandler |
| { |
| /** */ |
| #pragma warning disable 649 // unused field |
| [InstanceResource] private readonly IIgnite _ignite; |
| |
| /** <inheritdoc /> */ |
| public void OnLifecycleEvent(LifecycleEventType evt) |
| { |
| if (evt == LifecycleEventType.BeforeNodeStop && _ignite != null) |
| ((Ignite) _ignite).BeforeNodeStop(); |
| } |
| } |
| } |
| } |