blob: 3ffebc67b6c8d29ce1d80abc5e506f21227f894d [file] [log] [blame]
/*
* 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.
*/
package org.apache.ignite.internal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Handler;
import java.util.stream.Collectors;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteIllegalStateException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteState;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.Ignition;
import org.apache.ignite.IgnitionListener;
import org.apache.ignite.ShutdownPolicy;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.compute.ComputeJob;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.ConnectorConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.DeploymentMode;
import org.apache.ignite.configuration.ExecutorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.MemoryConfiguration;
import org.apache.ignite.configuration.MemoryPolicyConfiguration;
import org.apache.ignite.configuration.PersistentStoreConfiguration;
import org.apache.ignite.configuration.SystemDataRegionConfiguration;
import org.apache.ignite.configuration.TransactionConfiguration;
import org.apache.ignite.failure.FailureContext;
import org.apache.ignite.failure.FailureType;
import org.apache.ignite.internal.binary.BinaryArray;
import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader;
import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState;
import org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor;
import org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
import org.apache.ignite.internal.processors.metastorage.persistence.DistributedMetaStorageImpl;
import org.apache.ignite.internal.processors.resource.DependencyResolver;
import org.apache.ignite.internal.processors.resource.GridSpringResourceContext;
import org.apache.ignite.internal.util.GridConcurrentHashSet;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.util.TimeBag;
import org.apache.ignite.internal.util.spring.IgniteSpringHelper;
import org.apache.ignite.internal.util.typedef.CA;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.LT;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.util.worker.GridWorker;
import org.apache.ignite.internal.worker.WorkersRegistry;
import org.apache.ignite.lang.IgniteBiInClosure;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.marshaller.Marshaller;
import org.apache.ignite.marshaller.MarshallerUtils;
import org.apache.ignite.marshaller.jdk.JdkMarshaller;
import org.apache.ignite.mxbean.IgnitionMXBean;
import org.apache.ignite.plugin.segmentation.SegmentationPolicy;
import org.apache.ignite.resources.SpringApplicationContextResource;
import org.apache.ignite.spi.IgniteSpi;
import org.apache.ignite.spi.IgniteSpiMultipleInstancesSupport;
import org.apache.ignite.spi.checkpoint.noop.NoopCheckpointSpi;
import org.apache.ignite.spi.collision.noop.NoopCollisionSpi;
import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi;
import org.apache.ignite.spi.deployment.local.LocalDeploymentSpi;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;
import org.apache.ignite.spi.encryption.noop.NoopEncryptionSpi;
import org.apache.ignite.spi.eventstorage.NoopEventStorageSpi;
import org.apache.ignite.spi.failover.always.AlwaysFailoverSpi;
import org.apache.ignite.spi.indexing.noop.NoopIndexingSpi;
import org.apache.ignite.spi.loadbalancing.LoadBalancingSpi;
import org.apache.ignite.spi.loadbalancing.roundrobin.RoundRobinLoadBalancingSpi;
import org.apache.ignite.spi.metric.noop.NoopMetricExporterSpi;
import org.apache.ignite.spi.tracing.NoopTracingSpi;
import org.apache.ignite.thread.IgniteThread;
import org.jetbrains.annotations.Nullable;
import static java.util.stream.Collectors.joining;
import static org.apache.ignite.IgniteState.STARTED;
import static org.apache.ignite.IgniteState.STOPPED;
import static org.apache.ignite.IgniteState.STOPPED_ON_FAILURE;
import static org.apache.ignite.IgniteState.STOPPED_ON_SEGMENTATION;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_CACHE_CLIENT;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_CONFIG_URL;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_DEP_MODE_OVERRIDE;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_LOCAL_HOST;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_NO_SHUTDOWN_HOOK;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_OVERRIDE_CONSISTENT_ID;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_RESTART_CODE;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_SYSTEM_WORKER_BLOCKED_TIMEOUT;
import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
import static org.apache.ignite.cache.CacheMode.PARTITIONED;
import static org.apache.ignite.cache.CacheMode.REPLICATED;
import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
import static org.apache.ignite.configuration.MemoryConfiguration.DFLT_MEMORY_POLICY_MAX_SIZE;
import static org.apache.ignite.configuration.MemoryConfiguration.DFLT_MEM_PLC_DEFAULT_NAME;
import static org.apache.ignite.internal.IgniteComponentType.SPRING;
import static org.apache.ignite.internal.util.IgniteUtils.EMPTY_STRS;
import static org.apache.ignite.plugin.segmentation.SegmentationPolicy.RESTART_JVM;
/**
* This class is part of an internal API and can be modified at any time without backward compatibility.
*
* This class defines a factory for the main Ignite API. It controls Grid life cycle
* and allows listening for grid events.
* <h1 class="header">Grid Loaders</h1>
* Although user can apply grid factory directly to start and stop grid, grid is
* often started and stopped by grid loaders. Grid loaders can be found in
* {@link org.apache.ignite.startup} package, for example:
* <ul>
* <li>{@code CommandLineStartup}</li>
* <li>{@code ServletStartup}</li>
* </ul>
* <h1 class="header">Examples</h1>
* Use {@link #start()} method to start grid with default configuration. You can also use
* {@link IgniteConfiguration} to override some default configuration. Below is an
* example on how to start grid with <strong>URI deployment</strong>.
* <pre name="code" class="java">
* GridConfiguration cfg = new GridConfiguration();
*/
public class IgnitionEx {
/** Default configuration path relative to Ignite home. */
public static final String DFLT_CFG = "config/default-config.xml";
/** */
private static final int WAIT_FOR_BACKUPS_CHECK_INTERVAL = 1000;
/** Key to store list of gracefully stopping nodes within metastore. */
private static final String GRACEFUL_SHUTDOWN_METASTORE_KEY =
DistributedMetaStorageImpl.IGNITE_INTERNAL_KEY_PREFIX + "graceful.shutdown";
/** Map of named Ignite instances. */
private static final ConcurrentMap<Object, IgniteNamedInstance> grids = new ConcurrentHashMap<>();
/** Map of grid states ever started in this JVM. */
private static final Map<Object, IgniteState> gridStates = new ConcurrentHashMap<>();
/** Mutex to synchronize updates of default grid reference. */
private static final Object dfltGridMux = new Object();
/** Default grid. */
private static volatile IgniteNamedInstance dfltGrid;
/** Default grid state. */
private static volatile IgniteState dfltGridState;
/** List of state listeners. */
private static final Collection<IgnitionListener> lsnrs = new GridConcurrentHashSet<>(4);
/** */
private static ThreadLocal<Boolean> daemon = new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return false;
}
};
/** */
private static ThreadLocal<Boolean> clientMode = new ThreadLocal<>();
/** Dependency container. */
private static ThreadLocal<DependencyResolver> dependencyResolver = new ThreadLocal<>();
/**
* Enforces singleton.
*/
private IgnitionEx() {
// No-op.
}
/**
* Sets daemon flag.
* <p>
* If daemon flag is set then all grid instances created by the factory will be
* daemon, i.e. the local node for these instances will be a daemon node. Note that
* if daemon flag is set - it will override the same settings in {@link IgniteConfiguration#isDaemon()}.
* Note that you can set on and off daemon flag at will.
*
* @param daemon Daemon flag to set.
*/
public static void setDaemon(boolean daemon) {
IgnitionEx.daemon.set(daemon);
}
/**
* Gets daemon flag.
* <p>
* If daemon flag it set then all grid instances created by the factory will be
* daemon, i.e. the local node for these instances will be a daemon node. Note that
* if daemon flag is set - it will override the same settings in {@link IgniteConfiguration#isDaemon()}.
* Note that you can set on and off daemon flag at will.
*
* @return Daemon flag.
*/
public static boolean isDaemon() {
return daemon.get();
}
/**
* Sets client mode flag.
*
* @param clientMode Client mode flag.
*/
public static void setClientMode(boolean clientMode) {
IgnitionEx.clientMode.set(clientMode);
}
/**
* Gets client mode flag.
*
* @return Client mode flag.
*/
public static boolean isClientMode() {
return clientMode.get() == null ? false : clientMode.get();
}
/**
* Gets state of grid default grid.
*
* @return Default grid state.
*/
public static IgniteState state() {
return state(null);
}
/**
* Gets states of named Ignite instance. If name is {@code null}, then state of
* default no-name Ignite instance is returned.
*
* @param name Ignite instance name. If name is {@code null}, then state of
* default no-name Ignite instance is returned.
* @return Grid state.
*/
public static IgniteState state(@Nullable String name) {
IgniteNamedInstance grid = name != null ? grids.get(name) : dfltGrid;
if (grid == null) {
IgniteState state = name != null ? gridStates.get(name) : dfltGridState;
return state != null ? state : STOPPED;
}
return grid.state();
}
/**
* Stops default grid. This method is identical to {@code G.stop(null, cancel)} apply.
* Note that method does not wait for all tasks to be completed.
*
* @param cancel If {@code true} then all jobs currently executing on
* default grid will be cancelled by calling {@link ComputeJob#cancel()}
* method. Note that just like with {@link Thread#interrupt()}, it is
* up to the actual job to exit from execution
* @param shutdown If this parameter is set explicitly this policy will use for stopping node.
* If this parameter is {@code null} common cluster policy will be use.
* @return {@code true} if default grid instance was indeed stopped,
* {@code false} otherwise (if it was not started).
*/
public static boolean stop(boolean cancel, @Nullable ShutdownPolicy shutdown) {
return stop(null, cancel, shutdown, false);
}
/**
* Stops named Ignite instance. If {@code cancel} flag is set to {@code true} then
* all jobs currently executing on local node will be interrupted. If
* Ignite instance name is {@code null}, then default no-name Ignite instance will be stopped.
* If wait parameter is set to {@code true} then Ignite instance will wait for all
* tasks to be finished.
*
* @param name Ignite instance name. If {@code null}, then default no-name
* Ignite instance will be stopped.
* @param cancel If {@code true} then all jobs currently will be cancelled
* by calling {@link ComputeJob#cancel()} method. Note that just like with
* {@link Thread#interrupt()}, it is up to the actual job to exit from
* execution. If {@code false}, then jobs currently running will not be
* canceled. In either case, grid node will wait for completion of all
* jobs running on it before stopping.
* @param shutdown If this parameter is set explicitly this policy will use for stopping node.
* If this parameter is {@code null} common cluster policy will be use.
* @param stopNotStarted If {@code true} and node start did not finish then interrupts starting thread.
* @return {@code true} if named Ignite instance was indeed found and stopped,
* {@code false} otherwise (the instance with given {@code name} was
* not found).
*/
public static boolean stop(@Nullable String name, boolean cancel,
@Nullable ShutdownPolicy shutdown, boolean stopNotStarted) {
IgniteNamedInstance grid = name != null ? grids.get(name) : dfltGrid;
if (grid != null && stopNotStarted && grid.startLatch.getCount() != 0) {
grid.starterThreadInterrupted = true;
grid.starterThread.interrupt();
}
if (grid != null) {
if (grid.state() == STARTED)
grid.stop(cancel, shutdown);
boolean fireEvt;
if (name != null)
fireEvt = grids.remove(name, grid);
else {
synchronized (dfltGridMux) {
fireEvt = dfltGrid == grid;
if (fireEvt)
dfltGrid = null;
}
}
if (fireEvt)
notifyStateChange(grid.getName(), grid.state());
return true;
}
// We don't have log at this point...
U.warn(null, "Ignoring stopping Ignite instance that was already stopped or never started: " + name);
return false;
}
/**
* @deprecated
*
* Behavior of the method is the almost same as {@link IgnitionEx#stop(boolean, ShutdownPolicy)}.
* If node stopping process will not be finished within {@code timeoutMs} whole JVM will be killed.
*
* @param timeoutMs Timeout to wait graceful stopping.
*/
@Deprecated
public static boolean stop(@Nullable String name, boolean cancel, boolean stopNotStarted, long timeoutMs) {
final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// Schedule delayed node killing if graceful stopping will be not finished within timeout.
executor.schedule(new Runnable() {
@Override public void run() {
if (state(name) == IgniteState.STARTED) {
U.error(null, "Unable to gracefully stop node within timeout " + timeoutMs +
" milliseconds. Killing node...");
// We are not able to kill only one grid so whole JVM will be stopped.
Runtime.getRuntime().halt(Ignition.KILL_EXIT_CODE);
}
}
}, timeoutMs, TimeUnit.MILLISECONDS);
boolean success = stop(name, cancel, null, stopNotStarted);
executor.shutdownNow();
return success;
}
/**
* Stops <b>all</b> started grids. If {@code cancel} flag is set to {@code true} then
* all jobs currently executing on local node will be interrupted.
* If wait parameter is set to {@code true} then grid will wait for all
* tasks to be finished.
* <p>
* <b>Note:</b> it is usually safer and more appropriate to stop grid instances individually
* instead of blanket operation. In most cases, the party that started the grid instance
* should be responsible for stopping it.
*
* @param cancel If {@code true} then all jobs currently executing on
* all grids will be cancelled by calling {@link ComputeJob#cancel()}
* method. Note that just like with {@link Thread#interrupt()}, it is
* up to the actual job to exit from execution
* @param shutdown If this parameter is set explicitly this policy will use for stopping node.
* If this parameter is {@code null} common cluster policy will be use.
*/
public static void stopAll(boolean cancel, @Nullable ShutdownPolicy shutdown) {
IgniteNamedInstance dfltGrid0 = dfltGrid;
if (dfltGrid0 != null) {
dfltGrid0.stop(cancel, shutdown);
boolean fireEvt;
synchronized (dfltGridMux) {
fireEvt = dfltGrid == dfltGrid0;
if (fireEvt)
dfltGrid = null;
}
if (fireEvt)
notifyStateChange(dfltGrid0.getName(), dfltGrid0.state());
}
// Stop the rest and clear grids map.
for (IgniteNamedInstance grid : grids.values()) {
grid.stop(cancel, shutdown);
boolean fireEvt = grids.remove(grid.getName(), grid);
if (fireEvt)
notifyStateChange(grid.getName(), grid.state());
}
}
/**
* Restarts <b>all</b> started grids. If {@code cancel} flag is set to {@code true} then
* all jobs currently executing on the local node will be interrupted.
* If {@code wait} parameter is set to {@code true} then grid will wait for all
* tasks to be finished.
* <p>
* <b>Note:</b> it is usually safer and more appropriate to stop grid instances individually
* instead of blanket operation. In most cases, the party that started the grid instance
* should be responsible for stopping it.
* <p>
* Note also that restarting functionality only works with the tools that specifically
* support Ignite's protocol for restarting. Currently only standard <tt>ignite.{sh|bat}</tt>
* scripts support restarting of JVM Ignite's process.
*
* @param cancel If {@code true} then all jobs currently executing on
* all grids will be cancelled by calling {@link ComputeJob#cancel()}
* method. Note that just like with {@link Thread#interrupt()}, it is
* up to the actual job to exit from execution.
* @see Ignition#RESTART_EXIT_CODE
*/
public static void restart(boolean cancel) {
String file = U.IGNITE_SUCCESS_FILE_PROPERTY;
if (file == null)
U.warn(null, "Cannot restart node when restart not enabled.");
else {
try {
new File(file).createNewFile();
}
catch (IOException e) {
U.error(null, "Failed to create restart marker file (restart aborted): " + e.getMessage());
return;
}
U.log(null, "Restarting node. Will exit (" + Ignition.RESTART_EXIT_CODE + ").");
// Set the exit code so that shell process can recognize it and loop
// the start up sequence again.
System.setProperty(IGNITE_RESTART_CODE, Integer.toString(Ignition.RESTART_EXIT_CODE));
stopAll(cancel, null);
// This basically leaves loaders hang - we accept it.
System.exit(Ignition.RESTART_EXIT_CODE);
}
}
/**
* Stops <b>all</b> started grids. If {@code cancel} flag is set to {@code true} then
* all jobs currently executing on the local node will be interrupted.
* If {@code wait} parameter is set to {@code true} then grid will wait for all
* tasks to be finished.
* <p>
* <b>Note:</b> it is usually safer and more appropriate to stop grid instances individually
* instead of blanket operation. In most cases, the party that started the grid instance
* should be responsible for stopping it.
* <p>
* Note that upon completion of this method, the JVM with forcefully exist with
* exit code {@link Ignition#KILL_EXIT_CODE}.
*
* @param cancel If {@code true} then all jobs currently executing on
* all grids will be cancelled by calling {@link ComputeJob#cancel()}
* method. Note that just like with {@link Thread#interrupt()}, it is
* up to the actual job to exit from execution.
* @see Ignition#KILL_EXIT_CODE
*/
public static void kill(boolean cancel) {
stopAll(cancel, null);
// This basically leaves loaders hang - we accept it.
System.exit(Ignition.KILL_EXIT_CODE);
}
/**
* Starts grid with default configuration. By default this method will
* use grid configuration defined in {@code IGNITE_HOME/config/default-config.xml}
* configuration file. If such file is not found, then all system defaults will be used.
*
* @return Started grid.
* @throws IgniteCheckedException If default grid could not be started. This exception will be thrown
* also if default grid has already been started.
*/
public static Ignite start() throws IgniteCheckedException {
return start((GridSpringResourceContext)null);
}
/**
* Starts grid with default configuration. By default this method will
* use grid configuration defined in {@code IGNITE_HOME/config/default-config.xml}
* configuration file. If such file is not found, then all system defaults will be used.
*
* @param springCtx Optional Spring application context, possibly {@code null}.
* Spring bean definitions for bean injection are taken from this context.
* If provided, this context can be injected into grid tasks and grid jobs using
* {@link SpringApplicationContextResource @SpringApplicationContextResource} annotation.
* @return Started grid.
* @throws IgniteCheckedException If default grid could not be started. This exception will be thrown
* also if default grid has already been started.
*/
public static Ignite start(@Nullable GridSpringResourceContext springCtx) throws IgniteCheckedException {
URL url = U.resolveIgniteUrl(DFLT_CFG);
if (url != null)
return start(DFLT_CFG, null, springCtx, null);
U.warn(null, "Default Spring XML file not found (is IGNITE_HOME set?): " + DFLT_CFG);
return start0(new GridStartContext(new IgniteConfiguration(), null, springCtx), true)
.get1().grid();
}
/**
* Starts grid with given configuration. Note that this method is no-op if grid with the name
* provided in given configuration is already started.
*
* @param cfg Grid configuration. This cannot be {@code null}.
* @return Started grid.
* @throws IgniteCheckedException If grid could not be started. This exception will be thrown
* also if named grid has already been started.
*/
public static Ignite start(IgniteConfiguration cfg) throws IgniteCheckedException {
return start(cfg, null, true).get1();
}
/**
* Starts a grid with given configuration. If the grid is already started and failIfStarted set to TRUE
* an exception will be thrown.
*
* @param cfg Grid configuration. This cannot be {@code null}.
* @param failIfStarted When flag is {@code true} and grid with specified name has been already started
* the exception is thrown. Otherwise the existing instance of the grid is returned.
* @return Started grid or existing grid.
* @throws IgniteCheckedException If grid could not be started. This exception will be thrown
* also if named grid has already been started.
*/
public static Ignite start(IgniteConfiguration cfg, boolean failIfStarted) throws IgniteCheckedException {
return start(cfg, null, failIfStarted).get1();
}
/**
* Gets or starts new grid instance if it hasn't been started yet.
*
* @param cfg Grid configuration. This cannot be {@code null}.
* @return Tuple with: grid instance and flag to indicate the instance is started by this call.
* So, when the new ignite instance is started the flag is {@code true}. If an existing instance is returned
* the flag is {@code false}.
* @throws IgniteException If grid could not be started.
*/
public static T2<Ignite, Boolean> getOrStart(IgniteConfiguration cfg) throws IgniteException {
try {
return start(cfg, null, false);
}
catch (IgniteCheckedException e) {
throw U.convertException(e);
}
}
/**
* Starts grid with given configuration. Note that this method will throw and exception if grid with the name
* provided in given configuration is already started.
*
* @param cfg Grid configuration. This cannot be {@code null}.
* @param springCtx Optional Spring application context, possibly {@code null}.
* Spring bean definitions for bean injection are taken from this context.
* If provided, this context can be injected into grid tasks and grid jobs using
* {@link SpringApplicationContextResource @SpringApplicationContextResource} annotation.
* @return Started grid.
* @throws IgniteCheckedException If grid could not be started. This exception will be thrown
* also if named grid has already been started.
*/
public static Ignite start(IgniteConfiguration cfg, @Nullable GridSpringResourceContext springCtx) throws IgniteCheckedException {
A.notNull(cfg, "cfg");
return start0(new GridStartContext(cfg, null, springCtx), true).get1().grid();
}
/**
* Starts grid with given configuration. If the grid is already started and failIfStarted set to TRUE
* an exception will be thrown.
*
* @param cfg Grid configuration. This cannot be {@code null}.
* @param springCtx Optional Spring application context, possibly {@code null}.
* Spring bean definitions for bean injection are taken from this context.
* If provided, this context can be injected into grid tasks and grid jobs using
* {@link SpringApplicationContextResource @SpringApplicationContextResource} annotation.
* @param failIfStarted Throw or not an exception if grid is already started.
* @return Tuple with: grid instance and flag to indicate the instance is started by this call.
* So, when the new ignite instance is started the flag is {@code true}. If an existing instance is returned
* the flag is {@code false}.
* @throws IgniteCheckedException If grid could not be started. This exception will be thrown
* also if named grid has already been started.
*/
public static T2<Ignite, Boolean> start(
IgniteConfiguration cfg,
@Nullable GridSpringResourceContext springCtx,
boolean failIfStarted
) throws IgniteCheckedException {
A.notNull(cfg, "cfg");
T2<IgniteNamedInstance, Boolean> res = start0(new GridStartContext(cfg, null, springCtx), failIfStarted);
return new T2<>((Ignite)res.get1().grid(), res.get2());
}
/**
* Starts all grids specified within given Spring XML configuration file. If grid 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.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgPath Spring XML configuration file path or URL.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(@Nullable String springCfgPath) throws IgniteCheckedException {
return springCfgPath == null ? start() : start(springCfgPath, null);
}
/**
* Starts all grids specified within given Spring XML configuration file. If grid 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.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgPath Spring XML configuration file path or URL.
* @param igniteInstanceName Ignite instance name that will override default.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(@Nullable String springCfgPath, @Nullable String igniteInstanceName)
throws IgniteCheckedException {
if (springCfgPath == null) {
IgniteConfiguration cfg = new IgniteConfiguration();
if (cfg.getIgniteInstanceName() == null && !F.isEmpty(igniteInstanceName))
cfg.setIgniteInstanceName(igniteInstanceName);
return start(cfg);
}
else
return start(springCfgPath, igniteInstanceName, null, null);
}
/**
* Loads all grid configurations specified within given Spring XML configuration file.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgUrl Spring XML configuration file path or URL. This cannot be {@code null}.
* @return Tuple containing all loaded configurations and Spring context used to load them.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext>
loadConfigurations(URL springCfgUrl) throws IgniteCheckedException {
IgniteSpringHelper spring = SPRING.create(false);
return spring.loadConfigurations(springCfgUrl);
}
/**
* Loads all grid configurations specified within given input stream.
* <p>
* Usually Spring XML input stream will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration input stream by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgStream Input stream contained Spring XML configuration. This cannot be {@code null}.
* @return Tuple containing all loaded configurations and Spring context used to load them.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext>
loadConfigurations(InputStream springCfgStream) throws IgniteCheckedException {
IgniteSpringHelper spring = SPRING.create(false);
return spring.loadConfigurations(springCfgStream);
}
/**
* Loads all grid configurations specified within given Spring XML configuration file.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgPath Spring XML configuration file path. This cannot be {@code null}.
* @return Tuple containing all loaded configurations and Spring context used to load them.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext>
loadConfigurations(String springCfgPath) throws IgniteCheckedException {
A.notNull(springCfgPath, "springCfgPath");
return loadConfigurations(IgniteUtils.resolveSpringUrl(springCfgPath));
}
/**
* Loads first found grid configuration specified within given Spring XML configuration file.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgUrl Spring XML configuration file path or URL. This cannot be {@code null}.
* @return First found configuration and Spring context used to load it.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static IgniteBiTuple<IgniteConfiguration, GridSpringResourceContext> loadConfiguration(URL springCfgUrl)
throws IgniteCheckedException {
IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext> t =
loadConfigurations(springCfgUrl);
return F.t(F.first(t.get1()), t.get2());
}
/**
* Loads first found grid configuration specified within given Spring XML configuration file.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgPath Spring XML configuration file path. This cannot be {@code null}.
* @return First found configuration and Spring context used to load it.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static IgniteBiTuple<IgniteConfiguration, GridSpringResourceContext> loadConfiguration(String springCfgPath)
throws IgniteCheckedException {
IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext> t =
loadConfigurations(springCfgPath);
return F.t(F.first(t.get1()), t.get2());
}
/**
* Starts all grids specified within given Spring XML configuration file. If grid 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.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgPath Spring XML configuration file path or URL. This cannot be {@code null}.
* @param igniteInstanceName Ignite instance name that will override default.
* @param springCtx Optional Spring application context, possibly {@code null}.
* @param ldr Optional class loader that will be used by default.
* Spring bean definitions for bean injection are taken from this context.
* If provided, this context can be injected into grid tasks and grid jobs using
* {@link SpringApplicationContextResource @SpringApplicationContextResource} annotation.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(String springCfgPath, @Nullable String igniteInstanceName,
@Nullable GridSpringResourceContext springCtx, @Nullable ClassLoader ldr) throws IgniteCheckedException {
URL url = U.resolveSpringUrl(springCfgPath);
return start(url, igniteInstanceName, springCtx, ldr);
}
/**
* Starts all grids specified within given Spring XML configuration file URL. If grid 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.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgUrl Spring XML configuration file URL. This cannot be {@code null}.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(URL springCfgUrl) throws IgniteCheckedException {
return start(springCfgUrl, null, null, null);
}
/**
* Starts all grids specified within given Spring XML configuration file URL. If grid 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.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgUrl Spring XML configuration file URL. This cannot be {@code null}.
* @param ldr Optional class loader that will be used by default.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(URL springCfgUrl, @Nullable ClassLoader ldr) throws IgniteCheckedException {
return start(springCfgUrl, null, null, ldr);
}
/**
* Starts all grids specified within given Spring XML configuration file URL. If grid 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.
* <p>
* Usually Spring XML configuration file will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration file by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgUrl Spring XML configuration file URL. This cannot be {@code null}.
* @param igniteInstanceName Ignite instance name that will override default.
* @param springCtx Optional Spring application context, possibly {@code null}.
* @param ldr Optional class loader that will be used by default.
* Spring bean definitions for bean injection are taken from this context.
* If provided, this context can be injected into grid tasks and grid jobs using
* {@link SpringApplicationContextResource @SpringApplicationContextResource} annotation.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(URL springCfgUrl, @Nullable String igniteInstanceName,
@Nullable GridSpringResourceContext springCtx, @Nullable ClassLoader ldr) throws IgniteCheckedException {
A.notNull(springCfgUrl, "springCfgUrl");
Collection<Handler> savedHnds = U.addJavaNoOpLogger();
IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext> cfgMap;
try {
cfgMap = loadConfigurations(springCfgUrl);
}
finally {
U.removeJavaNoOpLogger(savedHnds);
}
return startConfigurations(cfgMap, springCfgUrl, igniteInstanceName, springCtx, ldr);
}
/**
* Starts all grids specified within given Spring XML configuration input stream. If grid 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.
* <p>
* Usually Spring XML configuration input stream will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration input stream by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgStream Input stream containing Spring XML configuration. This cannot be {@code null}.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(InputStream springCfgStream) throws IgniteCheckedException {
return start(springCfgStream, null, null, null);
}
/**
* Starts all grids specified within given Spring XML configuration input stream. If grid 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.
* <p>
* Usually Spring XML configuration input stream will contain only one Grid definition. Note that
* Grid configuration bean(s) is retrieved form configuration input stream by type, so the name of
* the Grid configuration bean is ignored.
*
* @param springCfgStream Input stream containing Spring XML configuration. This cannot be {@code null}.
* @param igniteInstanceName Ignite instance name that will override default.
* @param springCtx Optional Spring application context, possibly {@code null}.
* @param ldr Optional class loader that will be used by default.
* Spring bean definitions for bean injection are taken from this context.
* If provided, this context can be injected into grid tasks and grid jobs using
* {@link SpringApplicationContextResource @SpringApplicationContextResource} annotation.
* @return Started grid. If Spring configuration contains multiple grid instances,
* then the 1st found instance is returned.
* @throws IgniteCheckedException If grid could not be started or configuration
* read. This exception will be thrown also if grid with given name has already
* been started or Spring XML configuration file is invalid.
*/
public static Ignite start(InputStream springCfgStream, @Nullable String igniteInstanceName,
@Nullable GridSpringResourceContext springCtx, @Nullable ClassLoader ldr) throws IgniteCheckedException {
A.notNull(springCfgStream, "springCfgUrl");
Collection<Handler> savedHnds = U.addJavaNoOpLogger();
IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext> cfgMap;
try {
cfgMap = loadConfigurations(springCfgStream);
}
finally {
U.removeJavaNoOpLogger(savedHnds);
}
return startConfigurations(cfgMap, null, igniteInstanceName, springCtx, ldr);
}
/**
* Internal Spring-based start routine. Starts loaded configurations.
*
* @param cfgMap Configuration map.
* @param springCfgUrl Spring XML configuration file URL.
* @param igniteInstanceName Ignite instance name that will override default.
* @param springCtx Optional Spring application context.
* @param ldr Optional class loader that will be used by default.
* @return Started grid.
* @throws IgniteCheckedException If failed.
*/
private static Ignite startConfigurations(
IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext> cfgMap,
URL springCfgUrl,
@Nullable String igniteInstanceName,
@Nullable GridSpringResourceContext springCtx,
@Nullable ClassLoader ldr)
throws IgniteCheckedException {
List<IgniteNamedInstance> grids = new ArrayList<>(cfgMap.size());
try {
for (IgniteConfiguration cfg : cfgMap.get1()) {
assert cfg != null;
if (cfg.getIgniteInstanceName() == null && !F.isEmpty(igniteInstanceName))
cfg.setIgniteInstanceName(igniteInstanceName);
if (ldr != null && cfg.getClassLoader() == null)
cfg.setClassLoader(ldr);
// Use either user defined context or our one.
IgniteNamedInstance grid = start0(
new GridStartContext(cfg, springCfgUrl, springCtx == null
? cfgMap.get2() : springCtx), true).get1();
// Add it if it was not stopped during startup.
if (grid != null)
grids.add(grid);
}
}
catch (IgniteCheckedException e) {
// Stop all instances started so far.
for (IgniteNamedInstance grid : grids) {
try {
grid.stop(true, ShutdownPolicy.IMMEDIATE);
}
catch (Exception e1) {
U.error(grid.log, "Error when stopping grid: " + grid, e1);
}
}
throw e;
}
// Return the first grid started.
IgniteNamedInstance res = !grids.isEmpty() ? grids.get(0) : null;
return res != null ? res.grid() : null;
}
/**
* Starts grid with given configuration.
*
* @param startCtx Start context.
* @param failIfStarted Throw or not an exception if grid is already started.
* @return Tuple with: grid instance and flag to indicate the instance is started by this call.
* So, when the new ignite instance is started the flag is {@code true}. If an existing instance is returned
* the flag is {@code false}.
* @throws IgniteCheckedException If grid could not be started.
*/
private static T2<IgniteNamedInstance, Boolean> start0(
GridStartContext startCtx,
boolean failIfStarted
) throws IgniteCheckedException {
assert startCtx != null;
String name = startCtx.config().getIgniteInstanceName();
if (name != null && name.isEmpty())
throw new IgniteCheckedException("Non default Ignite instances cannot have empty string name.");
IgniteNamedInstance grid = new IgniteNamedInstance(name);
IgniteNamedInstance old;
if (name != null)
old = grids.putIfAbsent(name, grid);
else {
synchronized (dfltGridMux) {
old = dfltGrid;
if (old == null)
dfltGrid = grid;
}
}
if (old != null)
if (old.grid() == null) { // Stopped but not removed from map yet.
boolean replaced;
if (name != null)
replaced = grids.replace(name, old, grid);
else {
synchronized (dfltGridMux) {
replaced = old == dfltGrid;
if (replaced)
dfltGrid = grid;
}
}
if (!replaced) {
throw new IgniteCheckedException("Ignite instance with this name has been concurrently started: " +
name);
}
else
notifyStateChange(old.getName(), old.state());
}
else if (failIfStarted) {
if (name == null)
throw new IgniteCheckedException("Default Ignite instance has already been started.");
else
throw new IgniteCheckedException("Ignite instance with this name has already been started: " +
name);
}
else
return new T2<>(old, false);
if (startCtx.config().getWarmupClosure() != null)
startCtx.config().getWarmupClosure().apply(startCtx.config());
startCtx.single(grids.size() == 1);
boolean success = false;
try {
try {
grid.start(startCtx);
}
catch (Exception e) {
if (X.hasCause(e, IgniteInterruptedCheckedException.class, InterruptedException.class)) {
if (grid.starterThreadInterrupted)
Thread.interrupted();
}
throw e;
}
notifyStateChange(name, STARTED);
success = true;
}
finally {
if (!success) {
if (name != null)
grids.remove(name, grid);
else {
synchronized (dfltGridMux) {
if (dfltGrid == grid)
dfltGrid = null;
}
}
grid = null;
}
}
if (grid == null)
throw new IgniteCheckedException("Failed to start grid with provided configuration.");
return new T2<>(grid, true);
}
/**
* Loads spring bean by name.
*
* @param springXmlPath Spring XML file path.
* @param beanName Bean name.
* @return Bean instance.
* @throws IgniteCheckedException In case of error.
*/
public static <T> T loadSpringBean(String springXmlPath, String beanName) throws IgniteCheckedException {
A.notNull(springXmlPath, "springXmlPath");
A.notNull(beanName, "beanName");
URL url = U.resolveSpringUrl(springXmlPath);
assert url != null;
return loadSpringBean(url, beanName);
}
/**
* Loads spring bean by name.
*
* @param springXmlUrl Spring XML file URL.
* @param beanName Bean name.
* @return Bean instance.
* @throws IgniteCheckedException In case of error.
*/
public static <T> T loadSpringBean(URL springXmlUrl, String beanName) throws IgniteCheckedException {
A.notNull(springXmlUrl, "springXmlUrl");
A.notNull(beanName, "beanName");
IgniteSpringHelper spring = SPRING.create(false);
return spring.loadBean(springXmlUrl, beanName);
}
/**
* Loads spring bean by name.
*
* @param springXmlStream Input stream containing Spring XML configuration.
* @param beanName Bean name.
* @return Bean instance.
* @throws IgniteCheckedException In case of error.
*/
public static <T> T loadSpringBean(InputStream springXmlStream, String beanName) throws IgniteCheckedException {
A.notNull(springXmlStream, "springXmlPath");
A.notNull(beanName, "beanName");
IgniteSpringHelper spring = SPRING.create(false);
return spring.loadBean(springXmlStream, beanName);
}
/**
* Gets an instance of default no-name grid. Note that
* caller of this method should not assume that it will return the same
* instance every time.
*
* @return An instance of default no-name grid. This method never returns
* {@code null}.
* @throws IgniteIllegalStateException Thrown if default grid was not properly
* initialized or grid instance was stopped or was not started.
*/
public static Ignite grid() throws IgniteIllegalStateException {
return grid((String)null);
}
/**
* Gets a list of all grids started so far.
*
* @return List of all grids started so far.
*/
public static List<Ignite> allGrids() {
return allGrids(true);
}
/**
* Gets a list of all grids started so far.
*
* @return List of all grids started so far.
*/
public static List<Ignite> allGridsx() {
return allGrids(false);
}
/**
* Gets a list of all grids started so far.
*
* @param wait If {@code true} wait for node start finish.
* @return List of all grids started so far.
*/
private static List<Ignite> allGrids(boolean wait) {
List<Ignite> allIgnites = new ArrayList<>(grids.size() + 1);
for (IgniteNamedInstance grid : grids.values()) {
Ignite g = wait ? grid.grid() : grid.gridx();
if (g != null)
allIgnites.add(g);
}
IgniteNamedInstance dfltGrid0 = dfltGrid;
if (dfltGrid0 != null) {
IgniteKernal g = wait ? dfltGrid0.grid() : dfltGrid0.gridx();
if (g != null)
allIgnites.add(g);
}
return allIgnites;
}
/**
* Gets a grid instance for given local node ID. Note that grid instance and local node have
* one-to-one relationship where node has ID and instance has name of the grid to which
* both grid instance and its node belong. Note also that caller of this method
* should not assume that it will return the same instance every time.
*
* @param locNodeId ID of local node the requested grid instance is managing.
* @return An instance of named grid. This method never returns
* {@code null}.
* @throws IgniteIllegalStateException Thrown if grid was not properly
* initialized or grid instance was stopped or was not started.
*/
public static Ignite grid(UUID locNodeId) throws IgniteIllegalStateException {
A.notNull(locNodeId, "locNodeId");
IgniteNamedInstance dfltGrid0 = dfltGrid;
if (dfltGrid0 != null) {
IgniteKernal g = dfltGrid0.grid();
if (g != null && g.localNodeId().equals(locNodeId))
return g;
}
for (IgniteNamedInstance grid : grids.values()) {
IgniteKernal g = grid.grid();
if (g != null && g.localNodeId().equals(locNodeId))
return g;
}
throw new IgniteIllegalStateException("Grid instance with given local node ID was not properly " +
"started or was stopped: " + locNodeId);
}
/**
* Gets grid instance without waiting its initialization and not throwing any exception.
*
* @param locNodeId ID of local node the requested grid instance is managing.
* @return Grid instance or {@code null}.
*/
public static IgniteKernal gridxx(UUID locNodeId) {
IgniteNamedInstance dfltGrid0 = dfltGrid;
if (dfltGrid0 != null) {
IgniteKernal g = dfltGrid0.grid();
if (g != null && g.localNodeId().equals(locNodeId))
return g;
}
for (IgniteNamedInstance grid : grids.values()) {
IgniteKernal g = grid.grid();
if (g != null && g.localNodeId().equals(locNodeId))
return g;
}
return null;
}
/**
* Gets an named grid instance. If grid name is {@code null} or empty string,
* then default no-name grid will be returned. Note that caller of this method
* should not assume that it will return the same instance every time.
* <p>
* Note that Java VM can run multiple grid instances and every grid instance (and its
* node) can belong to a different grid. Grid name defines what grid a particular grid
* instance (and correspondingly its node) belongs to.
*
* @param name Grid name to which requested grid instance belongs to. If {@code null},
* then grid instance belonging to a default no-name grid will be returned.
* @return An instance of named grid. This method never returns
* {@code null}.
* @throws IgniteIllegalStateException Thrown if default grid was not properly
* initialized or grid instance was stopped or was not started.
*/
public static Ignite grid(@Nullable String name) throws IgniteIllegalStateException {
IgniteNamedInstance grid = name != null ? grids.get(name) : dfltGrid;
Ignite res;
if (grid == null || (res = grid.grid()) == null)
throw new IgniteIllegalStateException("Ignite instance with provided name doesn't exist. " +
"Did you call Ignition.start(..) to start an Ignite instance? [name=" + name + ']');
return res;
}
/**
* Gets a name of the grid from thread local config, which is owner of current thread.
*
* @return Grid instance related to current thread
* @throws IllegalArgumentException Thrown to indicate, that current thread is not an {@link IgniteThread}.
*/
public static IgniteKernal localIgnite() throws IllegalArgumentException {
String name = U.getCurrentIgniteName();
if (U.isCurrentIgniteNameSet(name))
return gridx(name);
else if (Thread.currentThread() instanceof IgniteThread)
return gridx(((IgniteThread)Thread.currentThread()).getIgniteInstanceName());
else
throw new IllegalArgumentException("Ignite instance name thread local must be set or" +
" this method should be accessed under " + IgniteThread.class.getName());
}
/**
* Gets grid instance without waiting its initialization.
*
* @param name Grid name.
* @return Grid instance.
*/
public static IgniteKernal gridx(@Nullable String name) {
IgniteNamedInstance grid = name != null ? grids.get(name) : dfltGrid;
IgniteKernal res;
if (grid == null || (res = grid.gridx()) == null)
throw new IgniteIllegalStateException("Ignite instance with provided name doesn't exist. " +
"Did you call Ignition.start(..) to start an Ignite instance? [name=" + name + ']');
return res;
}
/**
* Adds a lsnr for grid life cycle events.
* <p>
* Note that unlike other listeners in Ignite this listener will be
* notified from the same thread that triggers the state change. Because of
* that it is the responsibility of the user to make sure that listener logic
* is light-weight and properly handles (catches) any runtime exceptions, if any
* are expected.
*
* @param lsnr Listener for grid life cycle events. If this listener was already added
* this method is no-op.
*/
public static void addListener(IgnitionListener lsnr) {
A.notNull(lsnr, "lsnr");
lsnrs.add(lsnr);
}
/**
* Removes lsnr added by {@link #addListener(IgnitionListener)} method.
*
* @param lsnr Listener to remove.
* @return {@code true} if lsnr was added before, {@code false} otherwise.
*/
public static boolean removeListener(IgnitionListener lsnr) {
A.notNull(lsnr, "lsnr");
return lsnrs.remove(lsnr);
}
/**
* @param igniteInstanceName Ignite instance name.
* @param state Factory state.
*/
private static void notifyStateChange(@Nullable String igniteInstanceName, IgniteState state) {
if (igniteInstanceName != null)
gridStates.put(igniteInstanceName, state);
else
dfltGridState = state;
for (IgnitionListener lsnr : lsnrs)
lsnr.onStateChange(igniteInstanceName, state);
}
/**
* Sets custom dependency resolver which provides overridden dependencies
*
* @param rslvr Dependency resolver.
*/
public static void dependencyResolver(DependencyResolver rslvr) {
dependencyResolver.set(rslvr);
}
/**
* Custom dependency resolver.
*
* @return Returns {@code null} if resolver wasn't added.
*/
public static DependencyResolver dependencyResolver() {
return dependencyResolver.get();
}
/**
* @param name Grid name (possibly {@code null} for default grid).
* @return true when all managers, processors, and plugins have started and ignite kernal start method has fully
* completed.
*/
public static boolean hasKernalStarted(String name) {
IgniteNamedInstance grid = name != null ? grids.get(name) : dfltGrid;
return grid != null && grid.hasStartLatchCompleted();
}
/**
* Start context encapsulates all starting parameters.
*/
private static final class GridStartContext {
/** User-defined configuration. */
private IgniteConfiguration cfg;
/** Optional configuration path. */
private URL cfgUrl;
/** Optional Spring application context. */
private GridSpringResourceContext springCtx;
/** Whether or not this is a single grid instance in current VM. */
private boolean single;
/**
*
* @param cfg User-defined configuration.
* @param cfgUrl Optional configuration path.
* @param springCtx Optional Spring application context.
*/
GridStartContext(IgniteConfiguration cfg, @Nullable URL cfgUrl, @Nullable GridSpringResourceContext springCtx) {
assert (cfg != null);
this.cfg = cfg;
this.cfgUrl = cfgUrl;
this.springCtx = springCtx;
}
/**
* @return Whether or not this is a single grid instance in current VM.
*/
public boolean single() {
return single;
}
/**
* @param single Whether or not this is a single grid instance in current VM.
*/
public void single(boolean single) {
this.single = single;
}
/**
* @return User-defined configuration.
*/
IgniteConfiguration config() {
return cfg;
}
/**
* @param cfg User-defined configuration.
*/
void config(IgniteConfiguration cfg) {
this.cfg = cfg;
}
/**
* @return Optional configuration path.
*/
URL configUrl() {
return cfgUrl;
}
/**
* @param cfgUrl Optional configuration path.
*/
void configUrl(URL cfgUrl) {
this.cfgUrl = cfgUrl;
}
/**
* @return Optional Spring application context.
*/
public GridSpringResourceContext springContext() {
return springCtx;
}
}
/**
* Grid data container.
*/
private static final class IgniteNamedInstance {
/** Map of registered MBeans. */
private static final Map<MBeanServer, GridMBeanServerData> mbeans =
new HashMap<>();
/** Grid name. */
private final String name;
/** Grid instance. */
private volatile IgniteKernal grid;
/** Grid state. */
private volatile IgniteState state = STOPPED;
/** Shutdown hook. */
private Thread shutdownHook;
/** Grid log. */
private IgniteLogger log;
/** Start guard. */
private final AtomicBoolean startGuard = new AtomicBoolean();
/** Start latch. */
private final CountDownLatch startLatch = new CountDownLatch(1);
/** Raised if node is waiting graceful shutdown. Set to false to end wait. */
private volatile boolean delayedShutdown = false;
/**
* Thread that starts this named instance. This field can be non-volatile since
* it makes sense only for thread where it was originally initialized.
*/
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
private Thread starterThread;
/** */
private boolean starterThreadInterrupted;
/**
* Creates un-started named instance.
*
* @param name Grid name (possibly {@code null} for default grid).
*/
IgniteNamedInstance(@Nullable String name) {
this.name = name;
}
/**
* Gets grid name.
*
* @return Grid name.
*/
String getName() {
return name;
}
/**
* Gets grid instance.
*
* @return Grid instance.
*/
IgniteKernal grid() {
if (starterThread != Thread.currentThread())
U.awaitQuiet(startLatch);
return grid;
}
/**
* Gets grid instance without waiting for its initialization.
*
* @return Grid instance.
*/
public IgniteKernal gridx() {
return grid;
}
/**
* Gets grid state.
*
* @return Grid state.
*/
IgniteState state() {
if (starterThread != Thread.currentThread())
U.awaitQuiet(startLatch);
return state;
}
/**
* @param spi SPI implementation.
* @throws IgniteCheckedException Thrown in case if multi-instance is not supported.
*/
private void ensureMultiInstanceSupport(IgniteSpi spi) throws IgniteCheckedException {
IgniteSpiMultipleInstancesSupport ann = U.getAnnotation(spi.getClass(),
IgniteSpiMultipleInstancesSupport.class);
if (ann == null || !ann.value())
throw new IgniteCheckedException("SPI implementation doesn't support multiple grid instances in " +
"the same VM: " + spi);
}
/**
* @param spis SPI implementations.
* @throws IgniteCheckedException Thrown in case if multi-instance is not supported.
*/
private void ensureMultiInstanceSupport(IgniteSpi[] spis) throws IgniteCheckedException {
for (IgniteSpi spi : spis)
ensureMultiInstanceSupport(spi);
}
/**
* Starts grid with given configuration.
*
* @param startCtx Starting context.
* @throws IgniteCheckedException If start failed.
*/
synchronized void start(GridStartContext startCtx) throws IgniteCheckedException {
if (startGuard.compareAndSet(false, true)) {
try {
starterThread = Thread.currentThread();
IgniteConfiguration myCfg = initializeConfiguration(
startCtx.config() != null ? startCtx.config() : new IgniteConfiguration()
);
TimeBag startNodeTimer = new TimeBag(TimeUnit.MILLISECONDS, log.isInfoEnabled());
start0(startCtx, myCfg, startNodeTimer);
if (log.isInfoEnabled())
log.info("Node started : "
+ startNodeTimer.stagesTimings().stream().collect(joining(",", "[", "]")));
}
finally {
startLatch.countDown();
}
}
else
U.awaitQuiet(startLatch);
}
/**
* @param startCtx Starting context.
* @throws IgniteCheckedException If start failed.
*/
private void start0(GridStartContext startCtx, IgniteConfiguration cfg, TimeBag startTimer)
throws IgniteCheckedException {
assert grid == null : "Grid is already started: " + name;
// Set configuration URL, if any, into system property.
if (startCtx.configUrl() != null)
System.setProperty(IGNITE_CONFIG_URL, startCtx.configUrl().toString());
// Ensure that SPIs support multiple grid instances, if required.
if (!startCtx.single()) {
ensureMultiInstanceSupport(cfg.getDeploymentSpi());
ensureMultiInstanceSupport(cfg.getCommunicationSpi());
ensureMultiInstanceSupport(cfg.getDiscoverySpi());
ensureMultiInstanceSupport(cfg.getCheckpointSpi());
ensureMultiInstanceSupport(cfg.getEventStorageSpi());
ensureMultiInstanceSupport(cfg.getCollisionSpi());
ensureMultiInstanceSupport(cfg.getFailoverSpi());
ensureMultiInstanceSupport(cfg.getLoadBalancingSpi());
}
UncaughtExceptionHandler oomeHnd = new UncaughtExceptionHandler() {
@Override public void uncaughtException(Thread t, Throwable e) {
if (grid != null && X.hasCause(e, OutOfMemoryError.class))
grid.context().failure().process(new FailureContext(FailureType.CRITICAL_ERROR, e));
}
};
WorkersRegistry workerRegistry = new WorkersRegistry(
new IgniteBiInClosure<GridWorker, FailureType>() {
@Override public void apply(GridWorker worker, FailureType failureType) {
IgniteException ex = new IgniteException(S.toString(GridWorker.class, worker));
Thread runner = worker.runner();
if (runner != null && runner != Thread.currentThread())
ex.setStackTrace(runner.getStackTrace());
if (grid != null)
grid.context().failure().process(new FailureContext(failureType, ex));
}
},
IgniteSystemProperties.getLong(IGNITE_SYSTEM_WORKER_BLOCKED_TIMEOUT,
cfg.getSystemWorkerBlockedTimeout() != null
? cfg.getSystemWorkerBlockedTimeout()
: cfg.getFailureDetectionTimeout()),
log);
// Register Ignite MBean for current grid instance.
registerFactoryMbean(cfg.getMBeanServer());
boolean started = false;
try {
IgniteKernal grid0 = new IgniteKernal(startCtx.springContext());
// Init here to make grid available to lifecycle listeners.
grid = grid0;
startTimer.finishGlobalStage("Configure system pool");
grid0.start(
cfg,
new CA() {
@Override public void apply() {
startLatch.countDown();
}
},
workerRegistry,
oomeHnd,
startTimer
);
state = STARTED;
if (log.isDebugEnabled())
log.debug("Grid factory started ok: " + name);
started = true;
}
catch (IgniteCheckedException e) {
unregisterFactoryMBean();
throw e;
}
// Catch Throwable to protect against any possible failure.
catch (Throwable e) {
unregisterFactoryMBean();
if (e instanceof Error)
throw e;
throw new IgniteCheckedException("Unexpected exception when starting grid.", e);
}
finally {
if (!started)
// Grid was not started.
grid = null;
}
// Do NOT set it up only if IGNITE_NO_SHUTDOWN_HOOK=TRUE is provided.
if (!IgniteSystemProperties.getBoolean(IGNITE_NO_SHUTDOWN_HOOK, false)) {
try {
Runtime.getRuntime().addShutdownHook(shutdownHook = new Thread("shutdown-hook") {
@Override public void run() {
if (log.isInfoEnabled())
log.info("Invoking shutdown hook...");
IgniteNamedInstance ignite = IgniteNamedInstance.this;
ignite.stop(true, null);
}
});
if (log.isDebugEnabled())
log.debug("Shutdown hook is installed.");
}
catch (IllegalStateException e) {
stop(true, ShutdownPolicy.IMMEDIATE);
throw new IgniteCheckedException("Failed to install shutdown hook.", e);
}
}
else {
if (log.isDebugEnabled())
log.debug("Shutdown hook has not been installed because environment " +
"or system property " + IGNITE_NO_SHUTDOWN_HOOK + " is set.");
}
}
/**
* @param cfg Ignite configuration copy to.
* @return New ignite configuration.
* @throws IgniteCheckedException If failed.
*/
private IgniteConfiguration initializeConfiguration(IgniteConfiguration cfg)
throws IgniteCheckedException {
BinaryArray.initUseBinaryArrays();
IgniteConfiguration myCfg = new IgniteConfiguration(cfg);
String ggHome = cfg.getIgniteHome();
// Set Ignite home.
if (ggHome == null)
ggHome = U.getIgniteHome();
else
// If user provided IGNITE_HOME - set it as a system property.
U.setIgniteHome(ggHome);
String userProvidedWorkDir = cfg.getWorkDirectory();
// Correctly resolve work directory and set it back to configuration.
String workDir = U.workDirectory(userProvidedWorkDir, ggHome);
myCfg.setWorkDirectory(workDir);
// Ensure invariant.
// It's a bit dirty - but this is a result of late refactoring
// and I don't want to reshuffle a lot of code.
assert F.eq(name, cfg.getIgniteInstanceName());
UUID nodeId = cfg.getNodeId() != null ? cfg.getNodeId() : UUID.randomUUID();
myCfg.setNodeId(nodeId);
String predefineConsistentId = IgniteSystemProperties.getString(IGNITE_OVERRIDE_CONSISTENT_ID);
if (!F.isEmpty(predefineConsistentId))
myCfg.setConsistentId(predefineConsistentId);
IgniteLogger cfgLog = U.initLogger(cfg.getGridLogger(), null, nodeId, workDir);
assert cfgLog != null;
cfgLog = new GridLoggerProxy(cfgLog, null, name, U.id8(nodeId));
// Initialize factory's log.
log = cfgLog.getLogger(G.class);
myCfg.setGridLogger(cfgLog);
if (F.isEmpty(userProvidedWorkDir) && F.isEmpty(U.IGNITE_WORK_DIR))
log.warning("Ignite work directory is not provided, automatically resolved to: " + workDir);
// Check Ignite home folder (after log is available).
if (ggHome != null) {
File ggHomeFile = new File(ggHome);
if (!ggHomeFile.exists() || !ggHomeFile.isDirectory())
throw new IgniteCheckedException("Invalid Ignite installation home folder: " + ggHome);
}
myCfg.setIgniteHome(ggHome);
// Validate segmentation configuration.
SegmentationPolicy segPlc = cfg.getSegmentationPolicy();
// 1. Warn on potential configuration problem: grid is not configured to wait
// for correct segment after segmentation happens.
if (!F.isEmpty(cfg.getSegmentationResolvers()) && segPlc == RESTART_JVM && !cfg.isWaitForSegmentOnStart()) {
U.warn(log, "Found potential configuration problem (forgot to enable waiting for segment" +
"on start?) [segPlc=" + segPlc + ", wait=false]");
}
if (CU.isPersistenceEnabled(cfg) && myCfg.getConsistentId() == null)
U.warn(log, "Consistent ID is not set, it is recommended to set consistent ID for production " +
"clusters (use IgniteConfiguration.setConsistentId property)");
myCfg.setTransactionConfiguration(myCfg.getTransactionConfiguration() != null ?
new TransactionConfiguration(myCfg.getTransactionConfiguration()) : null);
myCfg.setConnectorConfiguration(myCfg.getConnectorConfiguration() != null ?
new ConnectorConfiguration(myCfg.getConnectorConfiguration()) : null);
// Local host.
String locHost = IgniteSystemProperties.getString(IGNITE_LOCAL_HOST);
myCfg.setLocalHost(F.isEmpty(locHost) ? myCfg.getLocalHost() : locHost);
// Override daemon flag if it was set on the factory.
if (daemon.get())
myCfg.setDaemon(true);
if (myCfg.isClientMode() == null) {
Boolean threadClient = clientMode.get();
if (threadClient == null)
myCfg.setClientMode(IgniteSystemProperties.getBoolean(IGNITE_CACHE_CLIENT, false));
else
myCfg.setClientMode(threadClient);
}
// Check for deployment mode override.
String depModeName = IgniteSystemProperties.getString(IGNITE_DEP_MODE_OVERRIDE);
if (!F.isEmpty(depModeName)) {
if (!F.isEmpty(myCfg.getCacheConfiguration())) {
U.quietAndInfo(log, "Skipping deployment mode override for caches (custom closure " +
"execution may not work for console Visor)");
}
else {
try {
DeploymentMode depMode = DeploymentMode.valueOf(depModeName);
if (myCfg.getDeploymentMode() != depMode)
myCfg.setDeploymentMode(depMode);
}
catch (IllegalArgumentException e) {
throw new IgniteCheckedException("Failed to override deployment mode using system property " +
"(are there any misspellings?)" +
"[name=" + IGNITE_DEP_MODE_OVERRIDE + ", value=" + depModeName + ']', e);
}
}
}
if (myCfg.getUserAttributes() == null)
myCfg.setUserAttributes(Collections.<String, Object>emptyMap());
initializeDefaultMBeanServer(myCfg);
Marshaller marsh = myCfg.getMarshaller();
if (marsh == null) {
if (!BinaryMarshaller.available()) {
U.warn(log, "Standard BinaryMarshaller can't be used on this JVM. " +
"Switch to HotSpot JVM or reach out Apache Ignite community for recommendations.");
marsh = new JdkMarshaller();
}
else
marsh = new BinaryMarshaller();
}
MarshallerUtils.setNodeName(marsh, cfg.getIgniteInstanceName());
myCfg.setMarshaller(marsh);
if (myCfg.getPeerClassLoadingLocalClassPathExclude() == null)
myCfg.setPeerClassLoadingLocalClassPathExclude(EMPTY_STRS);
initializeDefaultSpi(myCfg);
GridDiscoveryManager.initCommunicationErrorResolveConfiguration(myCfg);
initializeDefaultCacheConfiguration(myCfg);
ExecutorConfiguration[] execCfgs = myCfg.getExecutorConfiguration();
if (execCfgs != null) {
ExecutorConfiguration[] clone = execCfgs.clone();
for (int i = 0; i < execCfgs.length; i++)
clone[i] = new ExecutorConfiguration(execCfgs[i]);
myCfg.setExecutorConfiguration(clone);
}
initializeDataStorageConfiguration(myCfg);
return myCfg;
}
/**
* @param cfg Ignite configuration.
*/
private void initializeDataStorageConfiguration(IgniteConfiguration cfg) throws IgniteCheckedException {
if (cfg.getDataStorageConfiguration() != null &&
(cfg.getMemoryConfiguration() != null || cfg.getPersistentStoreConfiguration() != null)) {
throw new IgniteCheckedException("Data storage can be configured with either legacy " +
"(MemoryConfiguration, PersistentStoreConfiguration) or new (DataStorageConfiguration) classes, " +
"but not both.");
}
if (cfg.getMemoryConfiguration() != null || cfg.getPersistentStoreConfiguration() != null)
convertLegacyDataStorageConfigurationToNew(cfg);
if (!cfg.isClientMode() && cfg.getDataStorageConfiguration() == null)
cfg.setDataStorageConfiguration(new DataStorageConfiguration());
}
/**
* Initialize default cache configuration.
*
* @param cfg Ignite configuration.
* @throws IgniteCheckedException If failed.
*/
public void initializeDefaultCacheConfiguration(IgniteConfiguration cfg) throws IgniteCheckedException {
List<CacheConfiguration> cacheCfgs = new ArrayList<>();
cacheCfgs.add(utilitySystemCache());
CacheConfiguration[] userCaches = cfg.getCacheConfiguration();
if (userCaches != null && userCaches.length > 0) {
if (!U.discoOrdered(cfg.getDiscoverySpi()) && !U.relaxDiscoveryOrdered())
throw new IgniteCheckedException("Discovery SPI implementation does not support node ordering and " +
"cannot be used with cache (use SPI with @DiscoverySpiOrderSupport annotation, " +
"like TcpDiscoverySpi)");
for (CacheConfiguration ccfg : userCaches) {
if (CU.isReservedCacheName(ccfg.getName()))
throw new IgniteCheckedException("Cache name cannot be \"" + ccfg.getName() +
"\" because it is reserved for internal purposes.");
if (DataStructuresProcessor.isDataStructureCache(ccfg.getName()))
throw new IgniteCheckedException("Cache name cannot be \"" + ccfg.getName() +
"\" because it is reserved for data structures.");
cacheCfgs.add(ccfg);
}
}
cfg.setCacheConfiguration(cacheCfgs.toArray(new CacheConfiguration[cacheCfgs.size()]));
assert cfg.getCacheConfiguration() != null;
}
/**
* Initialize default SPI implementations.
*
* @param cfg Ignite configuration.
*/
private void initializeDefaultSpi(IgniteConfiguration cfg) {
if (cfg.getDiscoverySpi() == null)
cfg.setDiscoverySpi(new TcpDiscoverySpi());
if (cfg.getDiscoverySpi() instanceof TcpDiscoverySpi) {
TcpDiscoverySpi tcpDisco = (TcpDiscoverySpi)cfg.getDiscoverySpi();
if (tcpDisco.getIpFinder() == null)
tcpDisco.setIpFinder(new TcpDiscoveryMulticastIpFinder());
}
if (cfg.getCommunicationSpi() == null)
cfg.setCommunicationSpi(new TcpCommunicationSpi());
if (cfg.getDeploymentSpi() == null)
cfg.setDeploymentSpi(new LocalDeploymentSpi());
if (cfg.getEventStorageSpi() == null)
cfg.setEventStorageSpi(new NoopEventStorageSpi());
if (cfg.getCheckpointSpi() == null)
cfg.setCheckpointSpi(new NoopCheckpointSpi());
if (cfg.getCollisionSpi() == null)
cfg.setCollisionSpi(new NoopCollisionSpi());
if (cfg.getFailoverSpi() == null)
cfg.setFailoverSpi(new AlwaysFailoverSpi());
if (cfg.getLoadBalancingSpi() == null)
cfg.setLoadBalancingSpi(new RoundRobinLoadBalancingSpi());
else {
Collection<LoadBalancingSpi> spis = new ArrayList<>();
boolean dfltLoadBalancingSpi = false;
for (LoadBalancingSpi spi : cfg.getLoadBalancingSpi()) {
spis.add(spi);
if (!dfltLoadBalancingSpi && spi instanceof RoundRobinLoadBalancingSpi)
dfltLoadBalancingSpi = true;
}
// Add default load balancing SPI for internal tasks.
if (!dfltLoadBalancingSpi)
spis.add(new RoundRobinLoadBalancingSpi());
cfg.setLoadBalancingSpi(spis.toArray(new LoadBalancingSpi[spis.size()]));
}
if (cfg.getIndexingSpi() == null)
cfg.setIndexingSpi(new NoopIndexingSpi());
if (cfg.getEncryptionSpi() == null)
cfg.setEncryptionSpi(new NoopEncryptionSpi());
if (F.isEmpty(cfg.getMetricExporterSpi()))
cfg.setMetricExporterSpi(new NoopMetricExporterSpi());
if (cfg.getTracingSpi() == null)
cfg.setTracingSpi(new NoopTracingSpi());
}
/**
* Creates utility system cache configuration.
*
* @return Utility system cache configuration.
*/
private static CacheConfiguration utilitySystemCache() {
CacheConfiguration cache = new CacheConfiguration();
cache.setName(CU.UTILITY_CACHE_NAME);
cache.setCacheMode(REPLICATED);
cache.setAtomicityMode(TRANSACTIONAL);
cache.setRebalanceMode(SYNC);
cache.setWriteSynchronizationMode(FULL_SYNC);
cache.setAffinity(new RendezvousAffinityFunction(false, 100));
cache.setNodeFilter(CacheConfiguration.ALL_NODES);
cache.setRebalanceOrder(-2); //Prior to user caches.
cache.setCopyOnRead(false);
return cache;
}
/**
* Stops grid.
*
* @param cancel Flag indicating whether all currently running jobs
* should be cancelled.
* @param shutdown This is a policy of shutdown which is applied forcibly.
* If this property is {@code null}, present policy of the cluster will be used.
*/
void stop(boolean cancel, ShutdownPolicy shutdown) {
// Stop cannot be called prior to start from public API,
// since it checks for STARTED state. So, we can assert here.
assert startGuard.get();
if (shutdown == null)
shutdown = determineShutdownPolicy();
// If waiting for backups due to earlier invocation of stop(), stop wait and proceed shutting down.
if (shutdown == ShutdownPolicy.IMMEDIATE)
delayedShutdown = false;
stop0(cancel, shutdown);
}
/**
* Reads a policy from distributed meta storage or returns a default value if it isn't present.
*
* @return Shutdown policy.
*/
private ShutdownPolicy determineShutdownPolicy() {
if (IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_WAIT_FOR_BACKUPS_ON_SHUTDOWN))
return ShutdownPolicy.GRACEFUL;
return grid.cluster().shutdownPolicy();
}
/**
* Stops instance synchronously according to parameters.
*
* @param cancel Flag indicating whether all currently running job
* should be cancelled.
* @param shutdown Policy according to which shutdown will be performed.
*/
private synchronized void stop0(boolean cancel, ShutdownPolicy shutdown) {
IgniteKernal grid0 = grid;
// Double check.
if (grid0 == null) {
if (log != null)
U.warn(log, "Attempting to stop an already stopped Ignite instance (ignore): " + name);
return;
}
if (shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
shutdownHook = null;
if (log != null && log.isDebugEnabled())
log.debug("Shutdown hook is removed.");
}
catch (IllegalStateException e) {
// Shutdown is in progress...
if (log != null && log.isDebugEnabled())
log.debug("Shutdown is in progress (ignoring): " + e.getMessage());
}
}
if (shutdown == ShutdownPolicy.GRACEFUL && !grid.context().clientNode() && grid.cluster().active()) {
delayedShutdown = true;
if (log.isInfoEnabled())
log.info("Ensuring that caches have sufficient backups and local rebalance completion...");
DistributedMetaStorage metaStorage = grid.context().distributedMetastorage();
while (delayedShutdown) {
boolean safeToStop = true;
long topVer = grid.cluster().topologyVersion();
HashSet<UUID> originalNodesToExclude;
HashSet<UUID> nodesToExclude;
try {
originalNodesToExclude = metaStorage.read(GRACEFUL_SHUTDOWN_METASTORE_KEY);
nodesToExclude = originalNodesToExclude != null ? new HashSet<>(originalNodesToExclude) :
new HashSet<>();
}
catch (IgniteCheckedException e) {
U.error(log, "Unable to read " + GRACEFUL_SHUTDOWN_METASTORE_KEY +
" value from metastore.", e);
continue;
}
Map<UUID, Map<Integer, Set<Integer>>> proposedSuppliers = new HashMap<>();
for (CacheGroupContext grpCtx : grid.context().cache().cacheGroups()) {
if (grpCtx.systemCache())
continue;
if (grpCtx.config().getCacheMode() == PARTITIONED && grpCtx.config().getBackups() == 0) {
LT.warn(log, "Ignoring potential data loss on cache without backups [name="
+ grpCtx.cacheOrGroupName() + "]");
continue;
}
if (topVer != grpCtx.topology().readyTopologyVersion().topologyVersion()) {
// At the moment, there is an exchange.
safeToStop = false;
break;
}
GridDhtPartitionFullMap fullMap = grpCtx.topology().partitionMap(false);
if (fullMap == null) {
safeToStop = false;
break;
}
nodesToExclude.retainAll(fullMap.keySet());
if (!haveCopyLocalPartitions(grpCtx, nodesToExclude, proposedSuppliers)) {
safeToStop = false;
if (log.isInfoEnabled()) {
LT.info(log, "This node is waiting for backups of local partitions for group [id="
+ grpCtx.groupId() + ", name=" + grpCtx.cacheOrGroupName() + "]");
}
break;
}
if (!isRebalanceCompleted(grpCtx)) {
safeToStop = false;
if (log.isInfoEnabled()) {
LT.info(log, "This node is waiting for completion of rebalance for group [id="
+ grpCtx.groupId() + ", name=" + grpCtx.cacheOrGroupName() + "]");
}
break;
}
}
if (topVer != grid.cluster().topologyVersion())
safeToStop = false;
if (safeToStop && !proposedSuppliers.isEmpty()) {
Set<UUID> supportedPolicyNodes = proposedSuppliers.keySet().stream()
.filter(nodeId ->
IgniteFeatures.nodeSupports(grid0.cluster().node(nodeId), IgniteFeatures.SHUTDOWN_POLICY))
.collect(Collectors.toSet());
if (!supportedPolicyNodes.isEmpty()) {
safeToStop = grid0.compute(grid0.cluster().forNodeIds(supportedPolicyNodes))
.execute(CheckCpHistTask.class, proposedSuppliers);
}
}
if (safeToStop) {
try {
HashSet<UUID> newNodesToExclude = new HashSet<>(nodesToExclude);
newNodesToExclude.add(grid.localNodeId());
if (metaStorage.compareAndSet(GRACEFUL_SHUTDOWN_METASTORE_KEY, originalNodesToExclude,
newNodesToExclude))
break;
}
catch (IgniteCheckedException e) {
U.error(log, "Unable to write " + GRACEFUL_SHUTDOWN_METASTORE_KEY +
" value from metastore.", e);
continue;
}
}
try {
IgniteUtils.sleep(WAIT_FOR_BACKUPS_CHECK_INTERVAL);
}
catch (IgniteInterruptedCheckedException e) {
Thread.currentThread().interrupt();
}
}
}
// Unregister Ignite MBean.
unregisterFactoryMBean();
try {
grid0.stop(cancel);
if (log != null && log.isDebugEnabled())
log.debug("Ignite instance stopped ok: " + name);
}
catch (Throwable e) {
U.error(log, "Failed to properly stop grid instance due to undeclared exception.", e);
if (e instanceof Error)
throw e;
}
finally {
if (grid0.context().segmented())
state = STOPPED_ON_SEGMENTATION;
else if (grid0.context().invalid())
state = STOPPED_ON_FAILURE;
else
state = STOPPED;
grid = null;
log = null;
}
}
/**
* Checks, does the cluster have another copy of each local partition for specific group.
* Also, the method collects all nodes with can supply a local partition into {@code proposedSuppliers}.
*
* @param grpCtx Cahce group.
* @param nodesToExclude Nodes to exclude from check.
* @param proposedSuppliers Map of proposed suppliers for groups.
* @return True if all local partition of group specified have a copy in cluster, false otherwise.
*/
private boolean haveCopyLocalPartitions(
CacheGroupContext grpCtx,
Set<UUID> nodesToExclude,
Map<UUID, Map<Integer, Set<Integer>>> proposedSuppliers
) {
GridDhtPartitionFullMap fullMap = grpCtx.topology().partitionMap(false);
if (fullMap == null)
return false;
UUID localNodeId = grid.localNodeId();
GridDhtPartitionMap localPartMap = fullMap.get(localNodeId);
int parts = grpCtx.topology().partitions();
List<List<ClusterNode>> idealAssignment = grpCtx.affinity().idealAssignmentRaw();
for (int p = 0; p < parts; p++) {
if (localPartMap.get(p) != GridDhtPartitionState.OWNING)
continue;
boolean foundCopy = false;
for (Map.Entry<UUID, GridDhtPartitionMap> entry : fullMap.entrySet()) {
if (localNodeId.equals(entry.getKey()) || nodesToExclude.contains(entry.getKey()))
continue;
//This remote node does not present in ideal assignment.
if (!idealAssignment.get(p).stream().anyMatch(node -> node.id().equals(entry.getKey())))
continue;
//Rebalance in this cache.
if (entry.getValue().hasMovingPartitions())
continue;
if (entry.getValue().get(p) == GridDhtPartitionState.OWNING) {
proposedSuppliers.computeIfAbsent(entry.getKey(), (nodeId) -> new HashMap<>())
.computeIfAbsent(grpCtx.groupId(), grpId -> new HashSet<>())
.add(p);
foundCopy = true;
}
}
if (!foundCopy)
return false;
}
return true;
}
/**
* Check is rebalance completed for specific group on this node or not.
* It checks Demander and Supplier contexts.
*
* @param grpCtx Group context.
* @return True if rebalance completed, false otherwise.
*/
private boolean isRebalanceCompleted(CacheGroupContext grpCtx) {
if (!grpCtx.preloader().rebalanceFuture().isDone())
return false;
grpCtx.preloader().pause();
try {
return !((GridDhtPreloader)grpCtx.preloader()).supplier().isSupply();
}
finally {
grpCtx.preloader().resume();
}
}
/**
* Registers delegate Mbean instance for {@link Ignition}.
*
* @param srv MBeanServer where mbean should be registered.
* @throws IgniteCheckedException If registration failed.
*/
private void registerFactoryMbean(MBeanServer srv) throws IgniteCheckedException {
if (U.IGNITE_MBEANS_DISABLED)
return;
assert srv != null;
synchronized (mbeans) {
GridMBeanServerData data = mbeans.get(srv);
if (data == null) {
try {
IgnitionMXBean mbean = new IgnitionMXBeanAdapter();
ObjectName objName = U.makeMBeanName(
null,
"Kernal",
Ignition.class.getSimpleName()
);
// Make check if MBean was already registered.
if (!srv.queryMBeans(objName, null).isEmpty())
throw new IgniteCheckedException("MBean was already registered: " + objName);
else {
objName = U.registerMBean(
srv,
null,
"Kernal",
Ignition.class.getSimpleName(),
mbean,
IgnitionMXBean.class
);
data = new GridMBeanServerData(objName);
mbeans.put(srv, data);
if (log.isDebugEnabled())
log.debug("Registered MBean: " + objName);
}
}
catch (JMException e) {
throw new IgniteCheckedException("Failed to register MBean.", e);
}
}
assert data != null;
data.addIgniteInstance(name);
data.setCounter(data.getCounter() + 1);
}
}
/**
* Unregister delegate Mbean instance for {@link Ignition}.
*/
private void unregisterFactoryMBean() {
if (U.IGNITE_MBEANS_DISABLED)
return;
synchronized (mbeans) {
Iterator<Entry<MBeanServer, GridMBeanServerData>> iter = mbeans.entrySet().iterator();
while (iter.hasNext()) {
Entry<MBeanServer, GridMBeanServerData> entry = iter.next();
if (entry.getValue().containsIgniteInstance(name)) {
GridMBeanServerData data = entry.getValue();
assert data != null;
// Unregister MBean if no grid instances started for current MBeanServer.
if (data.getCounter() == 1) {
try {
entry.getKey().unregisterMBean(data.getMbean());
if (log.isDebugEnabled())
log.debug("Unregistered MBean: " + data.getMbean());
}
catch (JMException e) {
U.error(log, "Failed to unregister MBean.", e);
}
iter.remove();
}
else {
// Decrement counter.
data.setCounter(data.getCounter() - 1);
data.removeIgniteInstance(name);
}
}
}
}
}
/**
* Grid factory MBean data container.
* Contains necessary data for selected MBeanServer.
*/
private static class GridMBeanServerData {
/** Set of grid names for selected MBeanServer. */
private Collection<String> igniteInstanceNames = new HashSet<>();
/** */
private ObjectName mbean;
/** Count of grid instances. */
private int cnt;
/**
* Create data container.
*
* @param mbean Object name of MBean.
*/
GridMBeanServerData(ObjectName mbean) {
assert mbean != null;
this.mbean = mbean;
}
/**
* Add Ignite instance name.
*
* @param igniteInstanceName Ignite instance name.
*/
public void addIgniteInstance(String igniteInstanceName) {
igniteInstanceNames.add(igniteInstanceName);
}
/**
* Remove Ignite instance name.
*
* @param igniteInstanceName Ignite instance name.
*/
public void removeIgniteInstance(String igniteInstanceName) {
igniteInstanceNames.remove(igniteInstanceName);
}
/**
* Returns {@code true} if data contains the specified
* Ignite instance name.
*
* @param igniteInstanceName Ignite instance name.
* @return {@code true} if data contains the specified Ignite instance name.
*/
public boolean containsIgniteInstance(String igniteInstanceName) {
return igniteInstanceNames.contains(igniteInstanceName);
}
/**
* Gets name used in MBean server.
*
* @return Object name of MBean.
*/
public ObjectName getMbean() {
return mbean;
}
/**
* Gets number of grid instances working with MBeanServer.
*
* @return Number of grid instances.
*/
public int getCounter() {
return cnt;
}
/**
* Sets number of grid instances working with MBeanServer.
*
* @param cnt Number of grid instances.
*/
public void setCounter(int cnt) {
this.cnt = cnt;
}
}
/**
* @return whether the startLatch has been counted down, thereby indicating that the kernal has full started.
*/
public boolean hasStartLatchCompleted() {
return startLatch.getCount() == 0;
}
}
/** Initialize default mbean server. */
public static void initializeDefaultMBeanServer(IgniteConfiguration myCfg) {
if (myCfg.getMBeanServer() == null && !U.IGNITE_MBEANS_DISABLED)
myCfg.setMBeanServer(ManagementFactory.getPlatformMBeanServer());
}
/**
* @param cfg Ignite Configuration with legacy data storage configuration.
*/
private static void convertLegacyDataStorageConfigurationToNew(
IgniteConfiguration cfg) throws IgniteCheckedException {
PersistentStoreConfiguration psCfg = cfg.getPersistentStoreConfiguration();
boolean persistenceEnabled = psCfg != null;
DataStorageConfiguration dsCfg = new DataStorageConfiguration();
MemoryConfiguration memCfg = cfg.getMemoryConfiguration() != null ?
cfg.getMemoryConfiguration() : new MemoryConfiguration();
dsCfg.setConcurrencyLevel(memCfg.getConcurrencyLevel());
dsCfg.setPageSize(memCfg.getPageSize());
dsCfg.setSystemDataRegionConfiguration(
new SystemDataRegionConfiguration()
.setInitialSize(memCfg.getSystemCacheInitialSize())
.setMaxSize(memCfg.getSystemCacheMaxSize())
);
List<DataRegionConfiguration> optionalDataRegions = new ArrayList<>();
boolean customDfltPlc = false;
if (memCfg.getMemoryPolicies() != null) {
for (MemoryPolicyConfiguration mpc : memCfg.getMemoryPolicies()) {
DataRegionConfiguration region = new DataRegionConfiguration();
region.setPersistenceEnabled(persistenceEnabled);
if (mpc.getInitialSize() != 0L)
region.setInitialSize(mpc.getInitialSize());
region.setEmptyPagesPoolSize(mpc.getEmptyPagesPoolSize());
region.setEvictionThreshold(mpc.getEvictionThreshold());
region.setMaxSize(mpc.getMaxSize());
region.setName(mpc.getName());
region.setPageEvictionMode(mpc.getPageEvictionMode());
region.setMetricsRateTimeInterval(mpc.getRateTimeInterval());
region.setMetricsSubIntervalCount(mpc.getSubIntervals());
region.setSwapPath(mpc.getSwapFilePath());
region.setMetricsEnabled(mpc.isMetricsEnabled());
if (persistenceEnabled)
region.setCheckpointPageBufferSize(psCfg.getCheckpointingPageBufferSize());
if (mpc.getName() == null) {
throw new IgniteCheckedException(new IllegalArgumentException(
"User-defined MemoryPolicyConfiguration must have non-null and non-empty name."));
}
if (mpc.getName().equals(memCfg.getDefaultMemoryPolicyName())) {
customDfltPlc = true;
dsCfg.setDefaultDataRegionConfiguration(region);
}
else
optionalDataRegions.add(region);
}
}
if (!optionalDataRegions.isEmpty())
dsCfg.setDataRegionConfigurations(optionalDataRegions.toArray(
new DataRegionConfiguration[optionalDataRegions.size()]));
if (!customDfltPlc) {
if (!DFLT_MEM_PLC_DEFAULT_NAME.equals(memCfg.getDefaultMemoryPolicyName())) {
throw new IgniteCheckedException(new IllegalArgumentException("User-defined default MemoryPolicy " +
"name must be presented among configured MemoryPolices: " + memCfg.getDefaultMemoryPolicyName()));
}
dsCfg.setDefaultDataRegionConfiguration(new DataRegionConfiguration()
.setMaxSize(memCfg.getDefaultMemoryPolicySize())
.setName(memCfg.getDefaultMemoryPolicyName())
.setPersistenceEnabled(persistenceEnabled));
}
else {
if (memCfg.getDefaultMemoryPolicySize() != DFLT_MEMORY_POLICY_MAX_SIZE)
throw new IgniteCheckedException(new IllegalArgumentException("User-defined MemoryPolicy " +
"configuration and defaultMemoryPolicySize properties are set at the same time."));
}
if (persistenceEnabled) {
dsCfg.setCheckpointFrequency(psCfg.getCheckpointingFrequency());
dsCfg.setCheckpointThreads(psCfg.getCheckpointingThreads());
dsCfg.setCheckpointWriteOrder(psCfg.getCheckpointWriteOrder());
dsCfg.setFileIOFactory(psCfg.getFileIOFactory());
dsCfg.setLockWaitTime(psCfg.getLockWaitTime());
dsCfg.setStoragePath(psCfg.getPersistentStorePath());
dsCfg.setMetricsRateTimeInterval(psCfg.getRateTimeInterval());
dsCfg.setMetricsSubIntervalCount(psCfg.getSubIntervals());
dsCfg.setWalThreadLocalBufferSize(psCfg.getTlbSize());
dsCfg.setWalArchivePath(psCfg.getWalArchivePath());
dsCfg.setWalAutoArchiveAfterInactivity(psCfg.getWalAutoArchiveAfterInactivity());
dsCfg.setWalFlushFrequency(psCfg.getWalFlushFrequency());
dsCfg.setWalFsyncDelayNanos(psCfg.getWalFsyncDelayNanos());
dsCfg.setWalHistorySize(psCfg.getWalHistorySize());
dsCfg.setWalMode(psCfg.getWalMode());
dsCfg.setWalRecordIteratorBufferSize(psCfg.getWalRecordIteratorBufferSize());
dsCfg.setWalSegments(psCfg.getWalSegments());
dsCfg.setWalSegmentSize(psCfg.getWalSegmentSize());
dsCfg.setWalPath(psCfg.getWalStorePath());
dsCfg.setAlwaysWriteFullPages(psCfg.isAlwaysWriteFullPages());
dsCfg.setMetricsEnabled(psCfg.isMetricsEnabled());
dsCfg.setWriteThrottlingEnabled(psCfg.isWriteThrottlingEnabled());
}
cfg.setDataStorageConfiguration(dsCfg);
}
}