You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by se...@apache.org on 2015/02/02 02:53:30 UTC

[02/50] [abbrv] incubator-ignite git commit: GridUtils -> IgniteUtils

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/fc768b83/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
new file mode 100644
index 0000000..fedf24b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -0,0 +1,9141 @@
+/*
+ * 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.util;
+
+import org.apache.ignite.*;
+import org.apache.ignite.cache.*;
+import org.apache.ignite.cluster.*;
+import org.apache.ignite.compute.*;
+import org.apache.ignite.configuration.*;
+import org.apache.ignite.events.*;
+import org.apache.ignite.internal.*;
+import org.apache.ignite.internal.mxbean.*;
+import org.apache.ignite.internal.processors.cache.*;
+import org.apache.ignite.internal.processors.cache.version.*;
+import org.apache.ignite.lang.*;
+import org.apache.ignite.lifecycle.*;
+import org.apache.ignite.portables.*;
+import org.apache.ignite.spi.*;
+import org.apache.ignite.internal.managers.deployment.*;
+import org.apache.ignite.internal.processors.streamer.*;
+import org.apache.ignite.spi.discovery.*;
+import org.apache.ignite.internal.util.io.*;
+import org.apache.ignite.internal.util.lang.*;
+import org.apache.ignite.internal.util.typedef.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.internal.util.worker.*;
+import org.jdk8.backport.*;
+import org.jetbrains.annotations.*;
+import sun.misc.*;
+
+import javax.management.*;
+import javax.naming.*;
+import javax.net.ssl.*;
+import java.io.*;
+import java.lang.annotation.Annotation;
+import java.lang.management.*;
+import java.lang.reflect.Array;
+import java.lang.reflect.*;
+import java.math.*;
+import java.net.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.channels.spi.*;
+import java.nio.charset.*;
+import java.security.*;
+import java.security.cert.*;
+import java.sql.*;
+import java.sql.Timestamp;
+import java.text.*;
+import java.util.*;
+import java.util.Date;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import java.util.concurrent.locks.*;
+import java.util.jar.*;
+import java.util.logging.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+import static org.apache.ignite.IgniteSystemProperties.*;
+import static org.apache.ignite.events.IgniteEventType.*;
+import static org.apache.ignite.internal.GridNodeAttributes.*;
+
+/**
+ * Collection of utility methods used throughout the system.
+ */
+@SuppressWarnings({"UnusedReturnValue", "UnnecessaryFullyQualifiedName"})
+public abstract class IgniteUtils {
+    /** Unsafe. */
+    private static final Unsafe UNSAFE = GridUnsafe.unsafe();
+
+    /** {@code True} if {@code unsafe} should be used for array copy. */
+    private static final boolean UNSAFE_BYTE_ARR_CP = unsafeByteArrayCopyAvailable();
+
+    /** Offset. */
+    private static final int BYTE_ARRAY_DATA_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
+
+    /** Sun-specific JDK constructor factory for objects that don't have empty constructor. */
+    private static final Method CTOR_FACTORY;
+
+    /** Sun JDK reflection factory. */
+    private static final Object SUN_REFLECT_FACTORY;
+
+    /** Public {@code java.lang.Object} no-argument constructor. */
+    private static final Constructor OBJECT_CTOR;
+
+    /** All grid event names. */
+    private static final Map<Integer, String> GRID_EVT_NAMES = new HashMap<>();
+
+    /** All grid events. */
+    private static final int[] GRID_EVTS;
+
+    /** Empty integers array. */
+    private static final int[] EMPTY_INTS = new int[0];
+
+    /** Empty  longs. */
+    private static final long[] EMPTY_LONGS = new long[0];
+
+    /** System line separator. */
+    private static final String NL = System.getProperty("line.separator");
+
+    /** Default user version. */
+    public static final String DFLT_USER_VERSION = "0";
+
+    /** Cache for {@link GridPeerDeployAware} fields to speed up reflection. */
+    private static final ConcurrentMap<String, IgniteBiTuple<Class<?>, Collection<Field>>> p2pFields =
+        new ConcurrentHashMap8<>();
+
+    /** Secure socket protocol to use. */
+    private static final String HTTPS_PROTOCOL = "TLS";
+
+    /** Project home directory. */
+    private static volatile GridTuple<String> ggHome;
+
+    /** Project work directory. */
+    private static volatile String ggWork;
+
+    /** OS JDK string. */
+    private static String osJdkStr;
+
+    /** OS string. */
+    private static String osStr;
+
+    /** JDK string. */
+    private static String jdkStr;
+
+    /** Indicates whether current OS is Windows 95. */
+    private static boolean win95;
+
+    /** Indicates whether current OS is Windows 98. */
+    private static boolean win98;
+
+    /** Indicates whether current OS is Windows NT. */
+    private static boolean winNt;
+
+    /** Indicates whether current OS is Windows Vista. */
+    private static boolean winVista;
+
+    /** Indicates whether current OS is Windows 7. */
+    private static boolean win7;
+
+    /** Indicates whether current OS is Windows 8. */
+    private static boolean win8;
+
+    /** Indicates whether current OS is Windows 8.1. */
+    private static boolean win81;
+
+    /** Indicates whether current OS is some version of Windows. */
+    private static boolean unknownWin;
+
+    /** Indicates whether current OS is Windows 2000. */
+    private static boolean win2k;
+
+    /** Indicates whether current OS is Windows XP. */
+    private static boolean winXp;
+
+    /** Indicates whether current OS is Windows Server 2003. */
+    private static boolean win2003;
+
+    /** Indicates whether current OS is Windows Server 2008. */
+    private static boolean win2008;
+
+    /** Indicates whether current OS is UNIX flavor. */
+    private static boolean unix;
+
+    /** Indicates whether current OS is Solaris. */
+    private static boolean solaris;
+
+    /** Indicates whether current OS is Linux flavor. */
+    private static boolean linux;
+
+    /** Indicates whether current OS is NetWare. */
+    private static boolean netware;
+
+    /** Indicates whether current OS is Mac OS. */
+    private static boolean mac;
+
+    /** Indicates whether current OS architecture is Sun Sparc. */
+    private static boolean sparc;
+
+    /** Indicates whether current OS architecture is Intel X86. */
+    private static boolean x86;
+
+    /** Name of the underlying OS. */
+    private static String osName;
+
+    /** Version of the underlying OS. */
+    private static String osVer;
+
+    /** CPU architecture of the underlying OS. */
+    private static String osArch;
+
+    /** Name of the Java Runtime. */
+    private static String javaRtName;
+
+    /** Name of the Java Runtime version. */
+    private static String javaRtVer;
+
+    /** Name of the JDK vendor. */
+    private static String jdkVendor;
+
+    /** Name of the JDK. */
+    private static String jdkName;
+
+    /** Version of the JDK. */
+    private static String jdkVer;
+
+    /** Name of JVM specification. */
+    private static String jvmSpecName;
+
+    /** Version of JVM implementation. */
+    private static String jvmImplVer;
+
+    /** Vendor's name of JVM implementation. */
+    private static String jvmImplVendor;
+
+    /** Name of the JVM implementation. */
+    private static String jvmImplName;
+
+    /** JMX domain as 'xxx.gridgain'. */
+    public static final String JMX_DOMAIN = IgniteUtils.class.getName().substring(0, IgniteUtils.class.getName().
+        indexOf('.', IgniteUtils.class.getName().indexOf('.') + 1));
+
+    /** Network packet header. */
+    public static final byte[] GG_HEADER = U.intToBytes(0x00004747);
+
+    /** Default buffer size = 4K. */
+    private static final int BUF_SIZE = 4096;
+
+    /** Byte bit-mask. */
+    private static final int MASK = 0xf;
+
+    /** Long date format pattern for log messages. */
+    private static final SimpleDateFormat LONG_DATE_FMT = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+
+    /**
+     * Short date format pattern for log messages in "quiet" mode.
+     * Only time is included since we don't expect "quiet" mode to be used
+     * for longer runs.
+     */
+    private static final SimpleDateFormat SHORT_DATE_FMT = new SimpleDateFormat("HH:mm:ss");
+
+    /** Debug date format. */
+    private static final SimpleDateFormat DEBUG_DATE_FMT = new SimpleDateFormat("HH:mm:ss,SSS");
+
+    /** Cached local host address to make sure that every time the same local host is returned. */
+    private static InetAddress locHost;
+
+    /** */
+    static volatile long curTimeMillis = System.currentTimeMillis();
+
+    /** Primitive class map. */
+    private static final Map<String, Class<?>> primitiveMap = new HashMap<>(16, .5f);
+
+    /** Boxed class map. */
+    private static final Map<Class<?>, Class<?>> boxedClsMap = new HashMap<>(16, .5f);
+
+    /** Class loader used to load GridGain. */
+    private static final ClassLoader gridClassLoader = IgniteUtils.class.getClassLoader();
+
+    /** MAC OS invalid argument socket error message. */
+    public static final String MAC_INVALID_ARG_MSG = "On MAC OS you may have too many file descriptors open " +
+        "(simple restart usually solves the issue)";
+
+    /** Default help pages. */
+    public static final List<String> DFLT_HELP_LINKS = Arrays.asList(
+        "Troubleshooting:      http://bit.ly/GridGain-Troubleshooting",
+        "Documentation Center: http://bit.ly/GridGain-Documentation");
+
+    /** Portable classes. */
+    private static final Collection<Class<?>> PORTABLE_CLS = new HashSet<>();
+
+    /** GridGain Logging Directory. */
+    public static final String GRIDGAIN_LOG_DIR = System.getenv(GG_LOG_DIR);
+
+    /** GridGain Work Directory. */
+    public static final String GRIDGAIN_WORK_DIR = System.getenv(GG_WORK_DIR);
+
+    /** Clock timer. */
+    private static Thread timer;
+
+    /** Grid counter. */
+    private static int gridCnt;
+
+    /** Mutex. */
+    private static final Object mux = new Object();
+
+    /**
+     * Initializes enterprise check.
+     */
+    static {
+        String osName = System.getProperty("os.name");
+
+        String osLow = osName.toLowerCase();
+
+        // OS type detection.
+        if (osLow.contains("win")) {
+            if (osLow.contains("95"))
+                win95 = true;
+            else if (osLow.contains("98"))
+                win98 = true;
+            else if (osLow.contains("nt"))
+                winNt = true;
+            else if (osLow.contains("2000"))
+                win2k = true;
+            else if (osLow.contains("vista"))
+                winVista = true;
+            else if (osLow.contains("xp"))
+                winXp = true;
+            else if (osLow.contains("2003"))
+                win2003 = true;
+            else if (osLow.contains("2008"))
+                win2008 = true;
+            else if (osLow.contains("7"))
+                win7 = true;
+            else if (osLow.contains("8.1"))
+                win81 = true;
+            else if (osLow.contains("8"))
+                win8 = true;
+            else
+                unknownWin = true;
+        }
+        else if (osLow.contains("netware"))
+            netware = true;
+        else if (osLow.contains("mac os"))
+            mac = true;
+        else {
+            // UNIXs flavors tokens.
+            for (CharSequence os : new String[]{"ix", "inux", "olaris", "un", "ux", "sco", "bsd", "att"})
+                if (osLow.contains(os)) {
+                    unix = true;
+
+                    break;
+                }
+
+            // UNIX name detection.
+            if (osLow.contains("olaris"))
+                solaris = true;
+            else if (osLow.contains("inux"))
+                linux = true;
+        }
+
+        String osArch = System.getProperty("os.arch");
+
+        String archStr = osArch.toLowerCase();
+
+        // OS architecture detection.
+        if (archStr.contains("x86"))
+            x86 = true;
+        else if (archStr.contains("sparc"))
+            sparc = true;
+
+        String javaRtName = System.getProperty("java.runtime.name");
+        String javaRtVer = System.getProperty("java.runtime.version");
+        String jdkVendor = System.getProperty("java.specification.vendor");
+        String jdkName = System.getProperty("java.specification.name");
+        String jdkVer = System.getProperty("java.specification.version");
+        String osVer = System.getProperty("os.version");
+        String jvmSpecName = System.getProperty("java.vm.specification.name");
+        String jvmImplVer = System.getProperty("java.vm.version");
+        String jvmImplVendor = System.getProperty("java.vm.vendor");
+        String jvmImplName = System.getProperty("java.vm.name");
+
+        String jdkStr = javaRtName + ' ' + javaRtVer + ' ' + jvmImplVendor + ' ' + jvmImplName + ' ' +
+            jvmImplVer;
+
+        osStr = osName + ' ' + osVer + ' ' + osArch;
+        osJdkStr = osLow + ", " + jdkStr;
+
+        // Copy auto variables to static ones.
+        IgniteUtils.osName = osName;
+        IgniteUtils.jdkName = jdkName;
+        IgniteUtils.jdkVendor = jdkVendor;
+        IgniteUtils.jdkVer = jdkVer;
+        IgniteUtils.jdkStr = jdkStr;
+        IgniteUtils.osVer = osVer;
+        IgniteUtils.osArch = osArch;
+        IgniteUtils.jvmSpecName = jvmSpecName;
+        IgniteUtils.jvmImplVer = jvmImplVer;
+        IgniteUtils.jvmImplVendor = jvmImplVendor;
+        IgniteUtils.jvmImplName = jvmImplName;
+        IgniteUtils.javaRtName = javaRtName;
+        IgniteUtils.javaRtVer = javaRtVer;
+
+        primitiveMap.put("byte", byte.class);
+        primitiveMap.put("short", short.class);
+        primitiveMap.put("int", int.class);
+        primitiveMap.put("long", long.class);
+        primitiveMap.put("float", float.class);
+        primitiveMap.put("double", double.class);
+        primitiveMap.put("char", char.class);
+        primitiveMap.put("boolean", boolean.class);
+
+        boxedClsMap.put(byte.class, Byte.class);
+        boxedClsMap.put(short.class, Short.class);
+        boxedClsMap.put(int.class, Integer.class);
+        boxedClsMap.put(long.class, Long.class);
+        boxedClsMap.put(float.class, Float.class);
+        boxedClsMap.put(double.class, Double.class);
+        boxedClsMap.put(char.class, Character.class);
+        boxedClsMap.put(boolean.class, Boolean.class);
+
+        try {
+            OBJECT_CTOR = Object.class.getConstructor();
+        }
+        catch (NoSuchMethodException e) {
+            throw withCause(new AssertionError("Object class does not have empty constructor (is JDK corrupted?)."), e);
+        }
+
+        // Constructor factory.
+        Method ctorFac = null;
+        Object refFac = null;
+
+        try {
+            Class<?> refFactoryCls = Class.forName("sun.reflect.ReflectionFactory");
+
+            refFac = refFactoryCls.getMethod("getReflectionFactory").invoke(null);
+
+            ctorFac = refFac.getClass().getMethod("newConstructorForSerialization", Class.class,
+                Constructor.class);
+        }
+        catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InvocationTargetException ignored) {
+            // No-op.
+        }
+
+        CTOR_FACTORY = ctorFac;
+        SUN_REFLECT_FACTORY = refFac;
+
+        // Disable hostname SSL verification for development and testing with self-signed certificates.
+        if (Boolean.parseBoolean(System.getProperty(GG_DISABLE_HOSTNAME_VERIFIER))) {
+            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
+                @Override public boolean verify(String hostname, SSLSession sslSes) {
+                    return true;
+                }
+            });
+        }
+
+        // Event names initialization.
+        for (Field field : IgniteEventType.class.getFields()) {
+            if (field.getType().equals(int.class)) {
+                try {
+                    assert field.getName().startsWith("EVT_") : "Invalid event name (should start with 'EVT_': " +
+                        field.getName();
+
+                    int type = field.getInt(null);
+
+                    String prev = GRID_EVT_NAMES.put(type, field.getName().substring(4));
+
+                    // Check for duplicate event types.
+                    assert prev == null : "Duplicate event [type=" + type + ", name1=" + prev +
+                        ", name2=" + field.getName() + ']';
+                }
+                catch (IllegalAccessException e) {
+                    throw new IgniteException(e);
+                }
+            }
+        }
+
+        // Event array initialization.
+        GRID_EVTS = toIntArray(GRID_EVT_NAMES.keySet());
+
+        // Sort for fast event lookup.
+        Arrays.sort(GRID_EVTS);
+
+        // We need to re-initialize EVTS_ALL and EVTS_ALL_MINUS_METRIC_UPDATE
+        // because they may have been initialized to null before GRID_EVTS were initialized.
+        if (EVTS_ALL == null || EVTS_ALL_MINUS_METRIC_UPDATE == null) {
+            try {
+                Field f1 = IgniteEventType.class.getDeclaredField("EVTS_ALL");
+                Field f2 = IgniteEventType.class.getDeclaredField("EVTS_ALL_MINUS_METRIC_UPDATE");
+
+                assert f1 != null;
+                assert f2 != null;
+
+                // We use unsafe operations to update static fields on interface because
+                // they are treated as static final and cannot be updated via standard reflection.
+                UNSAFE.putObjectVolatile(UNSAFE.staticFieldBase(f1), UNSAFE.staticFieldOffset(f1), gridEvents());
+                UNSAFE.putObjectVolatile(UNSAFE.staticFieldBase(f2), UNSAFE.staticFieldOffset(f2),
+                    gridEvents(EVT_NODE_METRICS_UPDATED));
+
+                assert EVTS_ALL != null;
+                assert EVTS_ALL.length == GRID_EVTS.length;
+
+                assert EVTS_ALL_MINUS_METRIC_UPDATE != null;
+                assert EVTS_ALL_MINUS_METRIC_UPDATE.length == GRID_EVTS.length - 1;
+
+                // Validate correctness.
+                for (int type : GRID_EVTS) {
+                    assert containsIntArray(EVTS_ALL, type);
+
+                    if (type != EVT_NODE_METRICS_UPDATED)
+                        assert containsIntArray(EVTS_ALL_MINUS_METRIC_UPDATE, type);
+                }
+
+                assert !containsIntArray(EVTS_ALL_MINUS_METRIC_UPDATE, EVT_NODE_METRICS_UPDATED);
+            }
+            catch (NoSuchFieldException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        PORTABLE_CLS.add(Byte.class);
+        PORTABLE_CLS.add(Short.class);
+        PORTABLE_CLS.add(Integer.class);
+        PORTABLE_CLS.add(Long.class);
+        PORTABLE_CLS.add(Float.class);
+        PORTABLE_CLS.add(Double.class);
+        PORTABLE_CLS.add(Character.class);
+        PORTABLE_CLS.add(Boolean.class);
+        PORTABLE_CLS.add(String.class);
+        PORTABLE_CLS.add(UUID.class);
+        PORTABLE_CLS.add(Date.class);
+        PORTABLE_CLS.add(Timestamp.class);
+        PORTABLE_CLS.add(byte[].class);
+        PORTABLE_CLS.add(short[].class);
+        PORTABLE_CLS.add(int[].class);
+        PORTABLE_CLS.add(long[].class);
+        PORTABLE_CLS.add(float[].class);
+        PORTABLE_CLS.add(double[].class);
+        PORTABLE_CLS.add(char[].class);
+        PORTABLE_CLS.add(boolean[].class);
+        PORTABLE_CLS.add(String[].class);
+        PORTABLE_CLS.add(UUID[].class);
+        PORTABLE_CLS.add(Date[].class);
+        PORTABLE_CLS.add(Timestamp[].class);
+    }
+
+    /**
+     * @return System time approximated by 10 ms.
+     */
+    public static long currentTimeMillis() {
+        return curTimeMillis;
+    }
+
+    /**
+     * @return Value of {@link System#nanoTime()} in microseconds.
+     */
+    public static long microTime() {
+        return System.nanoTime() / 1000;
+    }
+
+    /**
+     * Gets nearest power of 2 larger or equal than v.
+     *
+     * @param v Value.
+     * @return Nearest power of 2.
+     */
+    public static int ceilPow2(int v) {
+        v--;
+
+        v |= v >> 1;
+        v |= v >> 2;
+        v |= v >> 4;
+        v |= v >> 8;
+        v |= v >> 16;
+
+        return ++v;
+    }
+
+    /**
+     * @param i Value.
+     * @return {@code true} If the given value is power of 2 (0 is not power of 2).
+     */
+    public static boolean isPow2(int i) {
+        return i > 0 && (i & (i - 1)) == 0;
+    }
+
+    /**
+     * Return SUN specific constructor factory.
+     *
+     * @return SUN specific constructor factory.
+     */
+    @Nullable public static Method ctorFactory() {
+        return CTOR_FACTORY;
+    }
+
+    /**
+     * @return Empty constructor for object class.
+     */
+    public static Constructor objectConstructor() {
+        return OBJECT_CTOR;
+    }
+
+    /**
+     * SUN JDK specific reflection factory for objects without public constructor.
+     *
+     * @return Reflection factory for objects without public constructor.
+     */
+    @Nullable public static Object sunReflectionFactory() {
+        return SUN_REFLECT_FACTORY;
+    }
+
+    /**
+     * Gets name for given grid event type.
+     *
+     * @param type Event type.
+     * @return Event name.
+     */
+    public static String gridEventName(int type) {
+        String name = GRID_EVT_NAMES.get(type);
+
+        return name != null ? name : Integer.toString(type);
+    }
+
+    /**
+     * Gets all event types.
+     *
+     * @param excl Optional exclude events.
+     * @return All events minus excluded ones.
+     */
+    public static int[] gridEvents(final int... excl) {
+        if (F.isEmpty(excl))
+            return GRID_EVTS;
+
+        List<Integer> evts = toIntList(GRID_EVTS, new P1<Integer>() {
+            @Override
+            public boolean apply(Integer i) {
+                return !containsIntArray(excl, i);
+            }
+        });
+
+        return toIntArray(evts);
+    }
+
+    /**
+     * @param discoSpi Discovery SPI.
+     * @return {@code True} if ordering is supported.
+     */
+    public static boolean discoOrdered(DiscoverySpi discoSpi) {
+        DiscoverySpiOrderSupport ann = U.getAnnotation(discoSpi.getClass(), DiscoverySpiOrderSupport.class);
+
+        return ann != null && ann.value();
+    }
+
+    /**
+     * @return Checks if disco ordering should be enforced.
+     */
+    public static boolean relaxDiscoveryOrdered() {
+        return "true".equalsIgnoreCase(System.getProperty(GG_NO_DISCO_ORDER));
+    }
+
+    /**
+     * This method should be used for adding quick debug statements in code
+     * while debugging. Calls to this method should never be committed to master.
+     *
+     * @param msg Message to debug.
+     * @deprecated Calls to this method should never be committed to master.
+     */
+    @Deprecated
+    public static void debug(Object msg) {
+        X.println(debugPrefix() + msg);
+    }
+
+    /**
+     * This method should be used for adding quick debug statements in code
+     * while debugging. Calls to this method should never be committed to master.
+     *
+     * @param msg Message to debug.
+     * @deprecated Calls to this method should never be committed to master.
+     */
+    @Deprecated
+    public static void debugx(String msg) {
+        X.printerrln(debugPrefix() + msg);
+    }
+
+    /**
+     * This method should be used for adding quick debug statements in code
+     * while debugging. Calls to this method should never be committed to master.
+     *
+     * @param log Logger.
+     * @param msg Message to debug.
+     *
+     * @deprecated Calls to this method should never be committed to master.
+     */
+    @Deprecated
+    public static void debug(IgniteLogger log, String msg) {
+        log.info(msg);
+    }
+
+    /**
+     * Prints stack trace of the current thread to {@code System.out}.
+     *
+     * @deprecated Calls to this method should never be committed to master.
+     */
+    @SuppressWarnings("deprecation")
+    @Deprecated
+    public static void dumpStack() {
+        dumpStack("Dumping stack.");
+    }
+
+    /**
+     * Prints stack trace of the current thread to {@code System.out}.
+     *
+     * @param msg Message to print with the stack.
+     *
+     * @deprecated Calls to this method should never be committed to master.
+     */
+    @Deprecated
+    public static void dumpStack(String msg) {
+        new Exception(debugPrefix() + msg).printStackTrace(System.out);
+    }
+
+    /**
+     * @param log Logger.
+     * @param msg Message.
+     */
+    public static void dumpStack(@Nullable IgniteLogger log, String msg) {
+        U.error(log, "Dumping stack.", new Exception(msg));
+    }
+
+    /**
+     * Prints stack trace of the current thread to provided output stream.
+     *
+     * @param msg Message to print with the stack.
+     * @param out Output to dump stack to.
+     *
+     * @deprecated Calls to this method should never be committed to master.
+     */
+    @Deprecated
+    public static void dumpStack(String msg, PrintStream out) {
+        new Exception(msg).printStackTrace(out);
+    }
+
+    /**
+     * Prints stack trace of the current thread to provided logger.
+     *
+     * @param log Logger.
+     * @param msg Message to print with the stack.
+     *
+     * @deprecated Calls to this method should never be committed to master.
+     */
+    @Deprecated
+    public static void debugStack(IgniteLogger log, String msg) {
+        log.error(msg, new Exception(debugPrefix() + msg));
+    }
+
+    /**
+     * @return Common prefix for debug messages.
+     */
+    private static String debugPrefix() {
+        return '<' + DEBUG_DATE_FMT.format(new Date(System.currentTimeMillis())) + "><DEBUG><" +
+            Thread.currentThread().getName() + '>' + ' ';
+    }
+
+    /**
+     * Prints heap usage.
+     */
+    public static void debugHeapUsage() {
+        System.gc();
+
+        Runtime runtime = Runtime.getRuntime();
+
+        X.println('<' + DEBUG_DATE_FMT.format(new Date(System.currentTimeMillis())) + "><DEBUG><" +
+            Thread.currentThread().getName() + "> Heap stats [free=" + runtime.freeMemory() / (1024 * 1024) +
+            "M, total=" + runtime.totalMemory() / (1024 * 1024) + "M]");
+    }
+
+    /**
+     * Gets heap size in GB rounded to specified precision.
+     *
+     * @param node Node.
+     * @param precision Precision.
+     * @return Heap size in GB.
+     */
+    public static double heapSize(ClusterNode node, int precision) {
+        return heapSize(Collections.singleton(node), precision);
+    }
+
+    /**
+     * Gets total heap size in GB rounded to specified precision.
+     *
+     * @param nodes Nodes.
+     * @param precision Precision.
+     * @return Total heap size in GB.
+     */
+    public static double heapSize(Iterable<ClusterNode> nodes, int precision) {
+        // In bytes.
+        double heap = 0.0;
+
+        for (ClusterNode n : nodesPerJvm(nodes)) {
+            ClusterMetrics m = n.metrics();
+
+            heap += Math.max(m.getHeapMemoryInitialized(), m.getHeapMemoryMaximum());
+        }
+
+        return roundedHeapSize(heap, precision);
+    }
+
+    /**
+     * Returns one representative node for each JVM.
+     *
+     * @param nodes Nodes.
+     * @return Collection which contains only one representative node for each JVM.
+     */
+    private static Iterable<ClusterNode> nodesPerJvm(Iterable<ClusterNode> nodes) {
+        Map<String, ClusterNode> grpMap = new HashMap<>();
+
+        // Group by mac addresses and pid.
+        for (ClusterNode node : nodes) {
+            String grpId = node.attribute(ATTR_MACS) + "|" + node.attribute(ATTR_JVM_PID);
+
+            if (!grpMap.containsKey(grpId))
+                grpMap.put(grpId, node);
+        }
+
+        return grpMap.values();
+    }
+
+    /**
+     * Returns current JVM maxMemory in the same format as {@link #heapSize(org.apache.ignite.cluster.ClusterNode, int)}.
+     *
+     * @param precision Precision.
+     * @return Maximum memory size in GB.
+     */
+    public static double heapSize(int precision) {
+        return roundedHeapSize(Runtime.getRuntime().maxMemory(), precision);
+    }
+
+    /**
+     * Rounded heap size in gigabytes.
+     *
+     * @param heap Heap.
+     * @param precision Precision.
+     * @return Rounded heap size.
+     */
+    private static double roundedHeapSize(double heap, int precision) {
+        double rounded = new BigDecimal(heap / (1024 * 1024 * 1024d)).round(new MathContext(precision)).doubleValue();
+
+        return rounded < 0.1 ? 0.1 : rounded;
+    }
+
+    /**
+     * Performs thread dump and prints all available info to the given log.
+     *
+     * @param log Logger.
+     */
+    public static void dumpThreads(@Nullable IgniteLogger log) {
+        ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
+
+        ThreadInfo[] threadInfos =
+            mxBean.dumpAllThreads(mxBean.isObjectMonitorUsageSupported(), mxBean.isSynchronizerUsageSupported());
+
+        GridStringBuilder sb = new GridStringBuilder("Thread dump at ")
+            .a(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(U.currentTimeMillis()))).a(NL);
+
+        for (ThreadInfo info : threadInfos) {
+            printThreadInfo(info, sb);
+
+            sb.a(NL);
+
+            if (info.getLockedSynchronizers() != null && info.getLockedSynchronizers().length > 0) {
+                printSynchronizersInfo(info.getLockedSynchronizers(), sb);
+
+                sb.a(NL);
+            }
+        }
+
+        sb.a(NL);
+
+        warn(log, sb.toString());
+    }
+
+    /**
+     * Prints single thread info to a buffer.
+     *
+     * @param threadInfo Thread info.
+     * @param sb Buffer.
+     */
+    private static void printThreadInfo(ThreadInfo threadInfo, GridStringBuilder sb) {
+        sb.a("Thread [name=\"").a(threadInfo.getThreadName())
+            .a("\", id=").a(threadInfo.getThreadId())
+            .a(", state=").a(threadInfo.getThreadState())
+            .a(", blockCnt=").a(threadInfo.getBlockedCount())
+            .a(", waitCnt=").a(threadInfo.getWaitedCount()).a("]").a(NL);
+
+        LockInfo lockInfo = threadInfo.getLockInfo();
+
+        if (lockInfo != null) {
+            sb.a("    Lock [object=").a(lockInfo)
+                .a(", ownerName=").a(threadInfo.getLockOwnerName())
+                .a(", ownerId=").a(threadInfo.getLockOwnerId()).a("]").a(NL);
+        }
+
+        MonitorInfo[] monitors = threadInfo.getLockedMonitors();
+        StackTraceElement[] elements = threadInfo.getStackTrace();
+
+        for (int i = 0; i < elements.length; i++) {
+            StackTraceElement e = elements[i];
+
+            sb.a("        at ").a(e.toString());
+
+            for (MonitorInfo monitor : monitors) {
+                if (monitor.getLockedStackDepth() == i)
+                    sb.a(NL).a("        - locked ").a(monitor);
+            }
+
+            sb.a(NL);
+        }
+    }
+
+    /**
+     * Prints Synchronizers info to a buffer.
+     *
+     * @param syncs Synchronizers info.
+     * @param sb Buffer.
+     */
+    private static void printSynchronizersInfo(LockInfo[] syncs, GridStringBuilder sb) {
+        sb.a("    Locked synchronizers:");
+
+        for (LockInfo info : syncs)
+            sb.a(NL).a("        ").a(info);
+    }
+
+    /**
+     * Gets empty constructor for class even if the class does not have empty constructor
+     * declared. This method is guaranteed to work with SUN JDK and other JDKs still need
+     * to be tested.
+     *
+     * @param cls Class to get empty constructor for.
+     * @return Empty constructor if one could be found or {@code null} otherwise.
+     * @throws IgniteCheckedException If failed.
+     */
+    @Nullable public static Constructor<?> forceEmptyConstructor(Class<?> cls) throws IgniteCheckedException {
+        Constructor<?> ctor = null;
+
+        try {
+            return cls.getDeclaredConstructor();
+        }
+        catch (Exception ignore) {
+            Method ctorFac = U.ctorFactory();
+            Object sunRefFac = U.sunReflectionFactory();
+
+            if (ctorFac != null && sunRefFac != null)
+                try {
+                    ctor = (Constructor)ctorFac.invoke(sunRefFac, cls, U.objectConstructor());
+                }
+                catch (IllegalAccessException | InvocationTargetException e) {
+                    throw new IgniteCheckedException("Failed to get object constructor for class: " + cls, e);
+                }
+        }
+
+        return ctor;
+    }
+
+    /**
+     * Creates new instance of a class only if it has an empty constructor (can be non-public).
+     *
+     * @param cls Class name.
+     * @return Instance.
+     * @throws IgniteCheckedException If failed.
+     */
+    @Nullable public static <T> T newInstance(String cls) throws IgniteCheckedException {
+        Class<?> cls0;
+
+        try {
+            cls0 = Class.forName(cls);
+        }
+        catch (Exception e) {
+            throw new IgniteCheckedException(e);
+        }
+
+        return (T)newInstance(cls0);
+    }
+
+    /**
+     * Creates new instance of a class only if it has an empty constructor (can be non-public).
+     *
+     * @param cls Class to instantiate.
+     * @return New instance of the class or {@code null} if empty constructor could not be assigned.
+     * @throws IgniteCheckedException If failed.
+     */
+    @Nullable public static <T> T newInstance(Class<T> cls) throws IgniteCheckedException {
+        boolean set = false;
+
+        Constructor<T> ctor = null;
+
+        try {
+            ctor = cls.getDeclaredConstructor();
+
+            if (ctor == null)
+                return null;
+
+            if (!ctor.isAccessible()) {
+                ctor.setAccessible(true);
+
+                set = true;
+            }
+
+            return ctor.newInstance();
+        }
+        catch (NoSuchMethodException e) {
+            throw new IgniteCheckedException("Failed to find empty constructor for class: " + cls, e);
+        }
+        catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
+            throw new IgniteCheckedException("Failed to create new instance for class: " + cls, e);
+        } finally {
+            if (ctor != null && set)
+                ctor.setAccessible(false);
+        }
+    }
+
+    /**
+     * Creates new instance of a class even if it does not have public constructor.
+     *
+     * @param cls Class to instantiate.
+     * @return New instance of the class or {@code null} if empty constructor could not be assigned.
+     * @throws IgniteCheckedException If failed.
+     */
+    @SuppressWarnings({"unchecked"})
+    @Nullable public static <T> T forceNewInstance(Class<?> cls) throws IgniteCheckedException {
+        Constructor ctor = forceEmptyConstructor(cls);
+
+        if (ctor == null)
+            return null;
+
+        boolean set = false;
+
+        try {
+
+            if (!ctor.isAccessible()) {
+                ctor.setAccessible(true);
+
+                set = true;
+            }
+
+            return (T)ctor.newInstance();
+        }
+        catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
+            throw new IgniteCheckedException("Failed to create new instance for class: " + cls, e);
+        } finally {
+            if (set)
+                ctor.setAccessible(false);
+        }
+    }
+
+    /**
+     * Pretty-formatting for minutes.
+     *
+     * @param mins Minutes to format.
+     * @return Formatted presentation of minutes.
+     */
+    public static String formatMins(long mins) {
+        assert mins >= 0;
+
+        if (mins == 0)
+            return "< 1 min";
+
+        SB sb = new SB();
+
+        long dd = mins / 1440; // 1440 mins = 60 mins * 24 hours
+
+        if (dd > 0)
+            sb.a(dd).a(dd == 1 ? " day " : " days ");
+
+        mins %= 1440;
+
+        long hh = mins / 60;
+
+        if (hh > 0)
+            sb.a(hh).a(hh == 1 ? " hour " : " hours ");
+
+        mins %= 60;
+
+        if (mins > 0)
+            sb.a(mins).a(mins == 1 ? " min " : " mins ");
+
+        return sb.toString().trim();
+    }
+
+    /**
+     * Gets 8-character substring of UUID (for terse logging).
+     *
+     * @param id Input ID.
+     * @return 8-character ID substring.
+     */
+    public static String id8(UUID id) {
+        return id.toString().substring(0, 8);
+    }
+
+    /**
+     * Gets 8-character substring of {@link org.apache.ignite.lang.IgniteUuid} (for terse logging).
+     * The ID8 will be constructed as follows:
+     * <ul>
+     * <li>Take first 4 digits for global ID, i.e. {@code GridUuid.globalId()}.</li>
+     * <li>Take last 4 digits for local ID, i.e. {@code GridUuid.localId()}.</li>
+     * </ul>
+     *
+     * @param id Input ID.
+     * @return 8-character representation of {@code GridUuid}.
+     */
+    public static String id8(IgniteUuid id) {
+        String s = id.toString();
+
+        return s.substring(0, 4) + s.substring(s.length() - 4);
+    }
+
+    /**
+     *
+     * @param len Number of characters to fill in.
+     * @param ch Character to fill with.
+     * @return String.
+     */
+    public static String filler(int len, char ch) {
+        char[] a = new char[len];
+
+        Arrays.fill(a, ch);
+
+        return new String(a);
+    }
+
+    /**
+     * Writes array to output stream.
+     *
+     * @param out Output stream.
+     * @param arr Array to write.
+     * @param <T> Array type.
+     * @throws IOException If failed.
+     */
+    public static <T> void writeArray(ObjectOutput out, T[] arr) throws IOException {
+        int len = arr == null ? 0 : arr.length;
+
+        out.writeInt(len);
+
+        if (arr != null && arr.length > 0)
+            for (T t : arr)
+                out.writeObject(t);
+    }
+
+    /**
+     * Reads array from input stream.
+     *
+     * @param in Input stream.
+     * @return Deserialized array.
+     * @throws IOException If failed.
+     * @throws ClassNotFoundException If class not found.
+     */
+    @Nullable public static Object[] readArray(ObjectInput in) throws IOException, ClassNotFoundException {
+        int len = in.readInt();
+
+        Object[] arr = null;
+
+        if (len > 0) {
+            arr = new Object[len];
+
+            for (int i = 0; i < len; i++)
+                arr[i] = in.readObject();
+        }
+
+        return arr;
+    }
+
+    /**
+     * Reads array from input stream.
+     *
+     * @param in Input stream.
+     * @return Deserialized array.
+     * @throws IOException If failed.
+     * @throws ClassNotFoundException If class not found.
+     */
+    @Nullable public static Class<?>[] readClassArray(ObjectInput in) throws IOException, ClassNotFoundException {
+        int len = in.readInt();
+
+        Class<?>[] arr = null;
+
+        if (len > 0) {
+            arr = new Class<?>[len];
+
+            for (int i = 0; i < len; i++)
+                arr[i] = (Class<?>)in.readObject();
+        }
+
+        return arr;
+    }
+
+    /**
+     * Reads array from input stream.
+     *
+     * @param in Input stream.
+     * @return Deserialized array.
+     * @throws IOException If failed.
+     * @throws ClassNotFoundException If class not found.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable public static <K, V> IgnitePredicate<CacheEntry<K, V>>[] readEntryFilterArray(ObjectInput in)
+        throws IOException, ClassNotFoundException {
+        int len = in.readInt();
+
+        IgnitePredicate<CacheEntry<K, V>>[] arr = null;
+
+        if (len > 0) {
+            arr = new IgnitePredicate[len];
+
+            for (int i = 0; i < len; i++)
+                arr[i] = (IgnitePredicate<CacheEntry<K, V>>)in.readObject();
+        }
+
+        return arr;
+    }
+
+    /**
+     *
+     * @param out Output.
+     * @param col Set to write.
+     * @throws IOException If write failed.
+     */
+    public static void writeCollection(ObjectOutput out, Collection<?> col) throws IOException {
+        if (col != null) {
+            out.writeInt(col.size());
+
+            for (Object o : col)
+                out.writeObject(o);
+        }
+        else
+            out.writeInt(-1);
+    }
+
+    /**
+     * Writes collection of byte arrays to data output.
+     *
+     * @param out Output to write to.
+     * @param bytes Collection with byte arrays.
+     * @throws IOException If write failed.
+     */
+    public static void writeBytesCollection(DataOutput out, Collection<byte[]> bytes) throws IOException {
+        if (bytes != null) {
+            out.writeInt(bytes.size());
+
+            for (byte[] b : bytes)
+                writeByteArray(out, b);
+        }
+        else
+            out.writeInt(-1);
+    }
+
+    /**
+     * Reads collection of byte arrays from data input.
+     *
+     * @param in Data input to read from.
+     * @return List of byte arrays.
+     * @throws IOException If read failed.
+     */
+    public static List<byte[]> readBytesList(DataInput in) throws IOException {
+        int size = in.readInt();
+
+        if (size < 0)
+            return null;
+
+        List<byte[]> res = new ArrayList<>(size);
+
+        for (int i = 0; i < size; i++)
+            res.add(readByteArray(in));
+
+        return res;
+    }
+
+    /**
+     *
+     * @param out Output.
+     * @param col Set to write.
+     * @throws IOException If write failed.
+     */
+    public static void writeIntCollection(DataOutput out, Collection<Integer> col) throws IOException {
+        if (col != null) {
+            out.writeInt(col.size());
+
+            for (Integer i : col)
+                out.writeInt(i);
+        }
+        else
+            out.writeInt(-1);
+    }
+
+    /**
+     * @param in Input.
+     * @return Deserialized set.
+     * @throws IOException If deserialization failed.
+     * @throws ClassNotFoundException If deserialized class could not be found.
+     */
+    @Nullable public static <E> Collection<E> readCollection(ObjectInput in)
+        throws IOException, ClassNotFoundException {
+        return readList(in);
+    }
+
+    /**
+     * @param in Input.
+     * @return Deserialized set.
+     * @throws IOException If deserialization failed.
+     */
+    @Nullable public static Collection<Integer> readIntCollection(DataInput in) throws IOException {
+        int size = in.readInt();
+
+        // Check null flag.
+        if (size == -1)
+            return null;
+
+        Collection<Integer> col = new ArrayList<>(size);
+
+        for (int i = 0; i < size; i++)
+            col.add(in.readInt());
+
+        return col;
+    }
+
+    /**
+     *
+     * @param m Map to copy.
+     * @param <K> Key type.
+     * @param <V> Value type
+     * @return Copied map.
+     */
+    public static <K, V> Map<K, V> copyMap(Map<K, V> m) {
+        return new HashMap<>(m);
+    }
+
+    /**
+     *
+     * @param m Map to seal.
+     * @param <K> Key type.
+     * @param <V> Value type
+     * @return Sealed map.
+     */
+    public static <K, V> Map<K, V> sealMap(Map<K, V> m) {
+        assert m != null;
+
+        return Collections.unmodifiableMap(new HashMap<>(m));
+    }
+
+    /**
+     * Seal collection.
+     *
+     * @param c Collection to seal.
+     * @param <E> Entry type
+     * @return Sealed collection.
+     */
+    public static <E> List<E> sealList(Collection<E> c) {
+        return Collections.unmodifiableList(new ArrayList<>(c));
+    }
+
+    /**
+     * Convert array to seal list.
+     *
+     * @param a Array for convert to seal list.
+     * @param <E> Entry type
+     * @return Sealed collection.
+     */
+    public static <E> List<E> sealList(E... a) {
+        return Collections.unmodifiableList(Arrays.asList(a));
+    }
+
+    /**
+     * Gets display name of the network interface this IP address belongs to.
+     *
+     * @param addr IP address for which to find network interface name.
+     * @return Network interface name or {@code null} if can't be found.
+     */
+    @Nullable public static String getNetworkInterfaceName(String addr) {
+        assert addr != null;
+
+        try {
+            InetAddress inetAddr = InetAddress.getByName(addr);
+
+            for (NetworkInterface itf : asIterable(NetworkInterface.getNetworkInterfaces()))
+                for (InetAddress itfAddr : asIterable(itf.getInetAddresses()))
+                    if (itfAddr.equals(inetAddr))
+                        return itf.getDisplayName();
+        }
+        catch (UnknownHostException ignore) {
+            return null;
+        }
+        catch (SocketException ignore) {
+            return null;
+        }
+
+        return null;
+    }
+
+    /**
+     * Tries to resolve host by name, returning local host if input is empty.
+     * This method reflects how {@link org.apache.ignite.configuration.IgniteConfiguration#getLocalHost()} should
+     * be handled in most places.
+     *
+     * @param hostName Hostname or {@code null} if local host should be returned.
+     * @return Address of given host or of localhost.
+     * @throws IOException If attempt to get local host failed.
+     */
+    public static InetAddress resolveLocalHost(@Nullable String hostName) throws IOException {
+        return F.isEmpty(hostName) ?
+            // Should default to InetAddress#anyLocalAddress which is package-private.
+            new InetSocketAddress(0).getAddress() :
+            InetAddress.getByName(hostName);
+    }
+
+    /**
+     * Determines whether current local host is different from previously cached.
+     *
+     * @return {@code true} or {@code false} depending on whether or not local host
+     *      has changed from the cached value.
+     * @throws IOException If attempt to get local host failed.
+     */
+    public static synchronized boolean isLocalHostChanged() throws IOException {
+        InetAddress locHost0 = locHost;
+
+        return locHost0 != null && !resetLocalHost().equals(locHost0);
+    }
+
+    /**
+     * Returns host names consistent with {@link #resolveLocalHost(String)}. So when it returns
+     * a common address this method returns single host name, and when a wildcard address passed
+     * this method tries to collect addresses of all available interfaces.
+     *
+     * @param locAddr Local address to resolve.
+     * @return Resolved available addresses of given local address.
+     * @throws IOException If failed.
+     * @throws IgniteCheckedException If no network interfaces found.
+     */
+    public static IgniteBiTuple<Collection<String>, Collection<String>> resolveLocalAddresses(InetAddress locAddr)
+        throws IOException, IgniteCheckedException {
+        assert locAddr != null;
+
+        Collection<String> addrs = new ArrayList<>();
+        Collection<String> hostNames = new ArrayList<>();
+
+        if (locAddr.isAnyLocalAddress()) {
+            // It should not take longer than 2 seconds to reach
+            // local address on any network.
+            int reachTimeout = 2000;
+
+            for (NetworkInterface itf : asIterable(NetworkInterface.getNetworkInterfaces())) {
+                for (InetAddress addr : asIterable(itf.getInetAddresses())) {
+                    if (!addr.isLinkLocalAddress() && reachable(itf, addr, reachTimeout))
+                        addresses(addr, addrs, hostNames);
+                }
+            }
+
+            if (F.isEmpty(addrs))
+                throw new IgniteCheckedException("No network addresses found (is networking enabled?).");
+        }
+        else
+            addresses(locAddr, addrs, hostNames);
+
+        return F.t(addrs, hostNames);
+    }
+
+    /**
+     * @param addr Address.
+     * @param addrs Addresses.
+     * @param hostNames Host names.
+     */
+    private static void addresses(InetAddress addr, Collection<String> addrs, Collection<String> hostNames) {
+        String hostName = addr.getHostName();
+
+        String ipAddr = addr.getHostAddress();
+
+        hostName = F.isEmpty(hostName) || hostName.equals(ipAddr) || addr.isLoopbackAddress() ? "" : hostName;
+
+        addrs.add(ipAddr);
+        hostNames.add(hostName);
+    }
+
+    /**
+     * Gets local host. Implementation will first attempt to get a non-loopback
+     * address. If that fails, then loopback address will be returned.
+     * <p>
+     * Note that this method is synchronized to make sure that local host
+     * initialization happens only once.
+     *
+     * @return Address representing local host.
+     * @throws IOException If attempt to get local host failed.
+     */
+    public static synchronized InetAddress getLocalHost() throws IOException {
+        if (locHost == null)
+            // Cache it.
+            resetLocalHost();
+
+        return locHost;
+    }
+
+    /**
+     * @return Local host.
+     * @throws IOException If attempt to get local host failed.
+     */
+    private static synchronized InetAddress resetLocalHost() throws IOException {
+        locHost = null;
+
+        String sysLocHost = IgniteSystemProperties.getString(GG_LOCAL_HOST);
+
+        if (sysLocHost != null)
+            sysLocHost = sysLocHost.trim();
+
+        if (!F.isEmpty(sysLocHost))
+            locHost = InetAddress.getByName(sysLocHost);
+        else {
+            List<NetworkInterface> itfs = new ArrayList<>();
+
+            for (NetworkInterface itf : asIterable(NetworkInterface.getNetworkInterfaces()))
+                itfs.add(itf);
+
+            Collections.sort(itfs, new Comparator<NetworkInterface>() {
+                @Override public int compare(NetworkInterface itf1, NetworkInterface itf2) {
+                    // Interfaces whose name starts with 'e' should go first.
+                    return itf1.getName().compareTo(itf2.getName());
+                }
+            });
+
+            // It should not take longer than 2 seconds to reach
+            // local address on any network.
+            int reachTimeout = 2000;
+
+            for (NetworkInterface itf : itfs) {
+                boolean found = false;
+
+                for (InetAddress addr : asIterable(itf.getInetAddresses())) {
+                    if (!addr.isLoopbackAddress() && !addr.isLinkLocalAddress() && reachable(itf, addr, reachTimeout)) {
+                        locHost = addr;
+
+                        found = true;
+
+                        break;
+                    }
+                }
+
+                if (found)
+                    break;
+            }
+        }
+
+        if (locHost == null)
+            locHost = InetAddress.getLocalHost();
+
+        return locHost;
+    }
+
+    /**
+     * Checks if address can be reached using three argument InetAddress.isReachable() version.
+     *
+     * @param itf Network interface to use for test.
+     * @param addr Address to check.
+     * @param reachTimeout Timeout for the check.
+     * @return {@code True} if address is reachable.
+     */
+    public static boolean reachable(NetworkInterface itf, InetAddress addr, int reachTimeout) {
+        try {
+            return addr.isReachable(itf, 0, reachTimeout);
+        }
+        catch (IOException ignore) {
+            return false;
+        }
+    }
+
+    /**
+     * Checks if address can be reached using one argument InetAddress.isReachable() version.
+     *
+     * @param addr Address to check.
+     * @param reachTimeout Timeout for the check.
+     * @return {@code True} if address is reachable.
+     */
+    public static boolean reachable(InetAddress addr, int reachTimeout) {
+        try {
+            return addr.isReachable(reachTimeout);
+        }
+        catch (IOException ignore) {
+            return false;
+        }
+    }
+
+    /**
+     * @param loc Local node.
+     * @param rmt Remote node.
+     * @return Whether given nodes have the same macs.
+     */
+    public static boolean sameMacs(ClusterNode loc, ClusterNode rmt) {
+        assert loc != null;
+        assert rmt != null;
+
+        String locMacs = loc.attribute(GridNodeAttributes.ATTR_MACS);
+        String rmtMacs = rmt.attribute(GridNodeAttributes.ATTR_MACS);
+
+        return locMacs != null && locMacs.equals(rmtMacs);
+    }
+
+    /**
+     * Gets a list of all local non-loopback IPs known to this JVM.
+     * Note that this will include both IPv4 and IPv6 addresses (even if one "resolves"
+     * into another). Loopbacks will be skipped.
+     *
+     * @return List of all known local IPs (empty list if no addresses available).
+     */
+    public static synchronized Collection<String> allLocalIps() {
+        List<String> ips = new ArrayList<>(4);
+
+        try {
+            Enumeration<NetworkInterface> itfs = NetworkInterface.getNetworkInterfaces();
+
+            if (itfs != null) {
+                for (NetworkInterface itf : asIterable(itfs)) {
+                    if (!itf.isLoopback()) {
+                        Enumeration<InetAddress> addrs = itf.getInetAddresses();
+
+                        if (addrs != null) {
+                            for (InetAddress addr : asIterable(addrs)) {
+                                String hostAddr = addr.getHostAddress();
+
+                                if (!addr.isLoopbackAddress() && !ips.contains(hostAddr))
+                                    ips.add(hostAddr);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        catch (SocketException ignore) {
+            return Collections.emptyList();
+        }
+
+        Collections.sort(ips);
+
+        return ips;
+    }
+
+    /**
+     * Gets a list of all local enabled MACs known to this JVM. It
+     * is using hardware address of the network interface that is not guaranteed to be
+     * MAC addresses (but in most cases it is).
+     * <p>
+     * Note that if network interface is disabled - its MAC won't be included. All
+     * local network interfaces are probed including loopbacks. Virtual interfaces
+     * (sub-interfaces) are skipped.
+     * <p>
+     * Note that on linux getHardwareAddress() can return null from time to time
+     * if NetworkInterface.getHardwareAddress() method is called from many threads.
+     *
+     * @return List of all known enabled local MACs or empty list
+     *      if no MACs could be found.
+     */
+    public static synchronized Collection<String> allLocalMACs() {
+        List<String> macs = new ArrayList<>(3);
+
+        try {
+            Enumeration<NetworkInterface> itfs = NetworkInterface.getNetworkInterfaces();
+
+            if (itfs != null) {
+                for (NetworkInterface itf : asIterable(itfs)) {
+                    byte[] hwAddr = itf.getHardwareAddress();
+
+                    // Loopback produces empty MAC.
+                    if (hwAddr != null && hwAddr.length > 0) {
+                        String mac = byteArray2HexString(hwAddr);
+
+                        if (!macs.contains(mac))
+                            macs.add(mac);
+                    }
+                }
+            }
+        }
+        catch (SocketException ignore) {
+            return Collections.emptyList();
+        }
+
+        Collections.sort(macs);
+
+        return macs;
+    }
+
+    /**
+     * Downloads resource by URL.
+     *
+     * @param url URL to download.
+     * @param file File where downloaded resource should be stored.
+     * @return File where downloaded resource should be stored.
+     * @throws IOException If error occurred.
+     */
+    public static File downloadUrl(URL url, File file) throws IOException {
+        assert url != null;
+        assert file != null;
+
+        InputStream in = null;
+        OutputStream out = null;
+
+        try {
+            URLConnection conn = url.openConnection();
+
+            if (conn instanceof HttpsURLConnection) {
+                HttpsURLConnection https = (HttpsURLConnection)conn;
+
+                https.setHostnameVerifier(new DeploymentHostnameVerifier());
+
+                SSLContext ctx = SSLContext.getInstance(HTTPS_PROTOCOL);
+
+                ctx.init(null, getTrustManagers(), null);
+
+                // Initialize socket factory.
+                https.setSSLSocketFactory(ctx.getSocketFactory());
+            }
+
+            in = conn.getInputStream();
+
+            if (in == null)
+                throw new IOException("Failed to open connection: " + url.toString());
+
+            out = new BufferedOutputStream(new FileOutputStream(file));
+
+            copy(in, out);
+        }
+        catch (NoSuchAlgorithmException | KeyManagementException e) {
+            throw new IOException("Failed to open HTTPs connection [url=" + url.toString() + ", msg=" + e + ']', e);
+        } finally {
+            close(in, null);
+            close(out, null);
+        }
+
+        return file;
+    }
+
+    /**
+     * Construct array with one trust manager which don't reject input certificates.
+     *
+     * @return Array with one X509TrustManager implementation of trust manager.
+     */
+    private static TrustManager[] getTrustManagers() {
+        return new TrustManager[]{
+            new X509TrustManager() {
+                @Nullable @Override public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                @Override public void checkClientTrusted(X509Certificate[] certs, String authType) {
+                    /* No-op. */
+                }
+
+                @Override public void checkServerTrusted(X509Certificate[] certs, String authType) {
+                    /* No-op. */
+                }
+            }
+        };
+    }
+
+    /**
+     * Replace password in URI string with a single '*' character.
+     * <p>
+     * Parses given URI by applying &quot;.*://(.*:.*)@.*&quot;
+     * regular expression pattern and than if URI matches it
+     * replaces password strings between '/' and '@' with '*'.
+     *
+     * @param uri URI which password should be replaced.
+     * @return Converted URI string
+     */
+    @Nullable public static String hidePassword(@Nullable String uri) {
+        if (uri == null)
+            return null;
+
+        if (Pattern.matches(".*://(.*:.*)@.*", uri)) {
+            int userInfoLastIdx = uri.indexOf('@');
+
+            assert userInfoLastIdx != -1;
+
+            String str = uri.substring(0, userInfoLastIdx);
+
+            int userInfoStartIdx = str.lastIndexOf('/');
+
+            str = str.substring(userInfoStartIdx + 1);
+
+            String[] params = str.split(";");
+
+            StringBuilder builder = new StringBuilder();
+
+            for (int i = 0; i < params.length; i++) {
+                int idx;
+
+                if ((idx = params[i].indexOf(':')) != -1)
+                    params[i] = params[i].substring(0, idx + 1) + '*';
+
+                builder.append(params[i]);
+
+                if (i != params.length - 1)
+                    builder.append(';');
+            }
+
+            return new StringBuilder(uri).replace(userInfoStartIdx + 1, userInfoLastIdx,
+                builder.toString()).toString();
+        }
+
+        return uri;
+    }
+
+    /**
+     * @return Class loader used to load GridGain itself.
+     */
+    public static ClassLoader gridClassLoader() {
+        return gridClassLoader;
+    }
+
+    /**
+     * @param parent Parent to find.
+     * @param ldr Loader to check.
+     * @return {@code True} if parent found.
+     */
+    public static boolean hasParent(@Nullable ClassLoader parent, ClassLoader ldr) {
+        if (parent != null) {
+            for (; ldr != null; ldr = ldr.getParent()) {
+                if (ldr.equals(parent))
+                    return true;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Verifier always returns successful result for any host.
+     */
+    private static class DeploymentHostnameVerifier implements HostnameVerifier {
+        /** {@inheritDoc} */
+        @Override public boolean verify(String hostname, SSLSession ses) {
+            // Remote host trusted by default.
+            return true;
+        }
+    }
+
+    /**
+     * Makes a {@code '+---+'} dash line.
+     *
+     * @param len Length of the dash line to make.
+     * @return Dash line.
+     */
+    public static String dash(int len) {
+        char[] dash = new char[len];
+
+        Arrays.fill(dash, '-');
+
+        dash[0] = dash[len - 1] = '+';
+
+        return new String(dash);
+    }
+
+    /**
+     * Creates space filled string of given length.
+     *
+     * @param len Number of spaces.
+     * @return Space filled string of given length.
+     */
+    public static String pad(int len) {
+        char[] dash = new char[len];
+
+        Arrays.fill(dash, ' ');
+
+        return new String(dash);
+    }
+
+    /**
+     * Formats system time in milliseconds for printing in logs.
+     *
+     * @param sysTime System time.
+     * @return Formatted time string.
+     */
+    public static String format(long sysTime) {
+        return LONG_DATE_FMT.format(new java.util.Date(sysTime));
+    }
+
+    /**
+     * Takes given collection, shuffles it and returns iterable instance.
+     *
+     * @param <T> Type of elements to create iterator for.
+     * @param col Collection to shuffle.
+     * @return Iterable instance over randomly shuffled collection.
+     */
+    public static <T> Iterable<T> randomIterable(Collection<T> col) {
+        List<T> list = new ArrayList<>(col);
+
+        Collections.shuffle(list);
+
+        return list;
+    }
+
+    /**
+     * Converts enumeration to iterable so it can be used in {@code foreach} construct.
+     *
+     * @param <T> Types of instances for iteration.
+     * @param e Enumeration to convert.
+     * @return Iterable over the given enumeration.
+     */
+    public static <T> Iterable<T> asIterable(final Enumeration<T> e) {
+        return new Iterable<T>() {
+            @Override public Iterator<T> iterator() {
+                return new Iterator<T>() {
+                    @Override public boolean hasNext() {
+                        return e.hasMoreElements();
+                    }
+
+                    @SuppressWarnings({"IteratorNextCanNotThrowNoSuchElementException"})
+                    @Override public T next() {
+                        return e.nextElement();
+                    }
+
+                    @Override public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    /**
+     * Copy source file (or folder) to destination file (or folder). Supported source & destination:
+     * <ul>
+     * <li>File to File</li>
+     * <li>File to Folder</li>
+     * <li>Folder to Folder (Copy the content of the directory and not the directory itself)</li>
+     * </ul>
+     *
+     * @param src Source file or folder.
+     * @param dest Destination file or folder.
+     * @param overwrite Whether or not overwrite existing files and folders.
+     * @throws IOException Thrown if an I/O error occurs.
+     */
+    public static void copy(File src, File dest, boolean overwrite) throws IOException {
+        assert src != null;
+        assert dest != null;
+
+        /*
+         * Supported source & destination:
+         * ===============================
+         * 1. File -> File
+         * 2. File -> Directory
+         * 3. Directory -> Directory
+         */
+
+        // Source must exist.
+        if (!src.exists())
+            throw new FileNotFoundException("Source can't be found: " + src);
+
+        // Check that source and destination are not the same.
+        if (src.getAbsoluteFile().equals(dest.getAbsoluteFile()))
+            throw new IOException("Source and destination are the same [src=" + src + ", dest=" + dest + ']');
+
+        if (dest.exists()) {
+            if (!dest.isDirectory() && !overwrite)
+                throw new IOException("Destination already exists: " + dest);
+
+            if (!dest.canWrite())
+                throw new IOException("Destination is not writable:" + dest);
+        }
+        else {
+            File parent = dest.getParentFile();
+
+            if (parent != null && !parent.exists())
+                // Ignore any errors here.
+                // We will get errors when we'll try to open the file stream.
+                //noinspection ResultOfMethodCallIgnored
+                parent.mkdirs();
+
+            // If source is a directory, we should create destination directory.
+            if (src.isDirectory())
+                //noinspection ResultOfMethodCallIgnored
+                dest.mkdir();
+        }
+
+        if (src.isDirectory()) {
+            // In this case we have Directory -> Directory.
+            // Note that we copy the content of the directory and not the directory itself.
+
+            File[] files = src.listFiles();
+
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    File dir = new File(dest, file.getName());
+
+                    if (!dir.exists() && !dir.mkdirs())
+                        throw new IOException("Can't create directory: " + dir);
+
+                    copy(file, dir, overwrite);
+                }
+                else
+                    copy(file, dest, overwrite);
+            }
+        }
+        else {
+            // In this case we have File -> File or File -> Directory.
+            File file = dest.exists() && dest.isDirectory() ? new File(dest, src.getName()) : dest;
+
+            if (!overwrite && file.exists())
+                throw new IOException("Destination already exists: " + file);
+
+            FileInputStream in = null;
+            FileOutputStream out = null;
+
+            try {
+                in = new FileInputStream(src);
+                out = new FileOutputStream(file);
+
+                copy(in, out);
+            }
+            finally {
+                if (in != null)
+                    in.close();
+
+                if (out != null) {
+                    out.getFD().sync();
+
+                    out.close();
+                }
+            }
+        }
+    }
+
+    /**
+     * Starts clock timer if grid is first.
+     */
+    public static void onGridStart() {
+        synchronized (mux) {
+            if (gridCnt == 0) {
+                timer = new Thread(new Runnable() {
+                    @SuppressWarnings({"BusyWait", "InfiniteLoopStatement"})
+                    @Override public void run() {
+                        while (true) {
+                            curTimeMillis = System.currentTimeMillis();
+
+                            try {
+                                Thread.sleep(10);
+                            }
+                            catch (InterruptedException ignored) {
+                                U.log(null, "Timer thread has been interrupted.");
+
+                                break;
+                            }
+                        }
+                    }
+                }, "gridgain-clock");
+
+                timer.setDaemon(true);
+
+                timer.setPriority(10);
+
+                timer.start();
+            }
+
+            ++gridCnt;
+        }
+    }
+
+    /**
+     * Stops clock timer if all nodes into JVM were stopped.
+     */
+    public static void onGridStop(){
+        synchronized (mux) {
+            assert gridCnt > 0 : gridCnt;
+
+            --gridCnt;
+
+            if (gridCnt == 0 && timer != null) {
+                timer.interrupt();
+
+                timer = null;
+            }
+        }
+    }
+
+    /**
+     * Copies input byte stream to output byte stream.
+     *
+     * @param in Input byte stream.
+     * @param out Output byte stream.
+     * @return Number of the copied bytes.
+     * @throws IOException Thrown if an I/O error occurs.
+     */
+    public static int copy(InputStream in, OutputStream out) throws IOException {
+        assert in != null;
+        assert out != null;
+
+        byte[] buf = new byte[BUF_SIZE];
+
+        int cnt = 0;
+
+        for (int n; (n = in.read(buf)) > 0;) {
+            out.write(buf, 0, n);
+
+            cnt += n;
+        }
+
+        return cnt;
+    }
+
+    /**
+     * Copies input character stream to output character stream.
+     *
+     * @param in Input character stream.
+     * @param out Output character stream.
+     * @return Number of the copied characters.
+     * @throws IOException Thrown if an I/O error occurs.
+     */
+    public static int copy(Reader in, Writer out) throws IOException {
+        assert in != null;
+        assert out != null;
+
+        char[] buf = new char[BUF_SIZE];
+
+        int cnt = 0;
+
+        for (int n; (n = in.read(buf)) > 0;) {
+            out.write(buf, 0, n);
+
+            cnt += n;
+        }
+
+        return cnt;
+    }
+
+    /**
+     * Writes string to file.
+     *
+     * @param file File.
+     * @param s String to write.
+     * @throws IOException Thrown if an I/O error occurs.
+     */
+    public static void writeStringToFile(File file, String s) throws IOException {
+        writeStringToFile(file, s, Charset.defaultCharset().toString(), false);
+    }
+
+    /**
+     * Writes string to file.
+     *
+     * @param file File.
+     * @param s String to write.
+     * @param charset Encoding.
+     * @throws IOException Thrown if an I/O error occurs.
+     */
+    public static void writeStringToFile(File file, String s, String charset) throws IOException {
+        writeStringToFile(file, s, charset, false);
+    }
+
+    /**
+     * Reads file to string using specified charset.
+     *
+     * @param fileName File name.
+     * @param charset File charset.
+     * @return File content.
+     * @throws IOException If error occurred.
+     */
+    public static String readFileToString(String fileName, String charset) throws IOException {
+        Reader input = new InputStreamReader(new FileInputStream(fileName), charset);
+
+        StringWriter output = new StringWriter();
+
+        char[] buf = new char[4096];
+
+        int n;
+
+        while ((n = input.read(buf)) != -1)
+            output.write(buf, 0, n);
+
+        return output.toString();
+    }
+
+    /**
+     * Writes string to file.
+     *
+     * @param file File.
+     * @param s String to write.
+     * @param charset Encoding.
+     * @param append If {@code true}, then specified string will be added to the end of the file.
+     * @throws IOException Thrown if an I/O error occurs.
+     */
+    public static void writeStringToFile(File file, String s, String charset, boolean append) throws IOException {
+        if (s == null)
+            return;
+
+        OutputStream out = null;
+
+        try {
+            out = new FileOutputStream(file, append);
+
+            if (s != null)
+                out.write(s.getBytes(charset));
+        } finally {
+            closeQuiet(out);
+        }
+    }
+
+    /**
+     * Utility method that sets cause into exception and returns it.
+     *
+     * @param e Exception to set cause to and return.
+     * @param cause Optional cause to set (if not {@code null}).
+     * @param <E> Type of the exception.
+     * @return Passed in exception with optionally set cause.
+     */
+    public static <E extends Throwable> E withCause(E e, @Nullable Throwable cause) {
+        assert e != null;
+
+        if (cause != null)
+            e.initCause(cause);
+
+        return e;
+    }
+
+    /**
+     * Deletes file or directory with all sub-directories and files.
+     *
+     * @param file File or directory to delete.
+     * @return {@code true} if and only if the file or directory is successfully deleted,
+     *      {@code false} otherwise
+     */
+    public static boolean delete(File file) {
+        assert file != null;
+
+        boolean res = true;
+
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+
+            if (files != null && files.length > 0)
+                for (File file1 : files)
+                    if (file1.isDirectory())
+                        res &= delete(file1);
+                    else if (file1.getName().endsWith("jar"))
+                        try {
+                            // Why do we do this?
+                            new JarFile(file1, false).close();
+
+                            res &= file1.delete();
+                        }
+                        catch (IOException ignore) {
+                            // Ignore it here...
+                        }
+                    else
+                        res &= file1.delete();
+
+            res &= file.delete();
+        }
+        else
+            res = file.delete();
+
+        return res;
+    }
+
+    /**
+     * @param dir Directory to create along with all non-existent parent directories.
+     * @return {@code True} if directory exists (has been created or already existed),
+     *      {@code false} if has not been created and does not exist.
+     */
+    public static boolean mkdirs(File dir) {
+        assert dir != null;
+
+        return dir.mkdirs() || dir.exists();
+    }
+
+    /**
+     * Resolve project home directory based on source code base.
+     *
+     * @return Project home directory (or {@code null} if it cannot be resolved).
+     */
+    @Nullable private static String resolveProjectHome() {
+        assert Thread.holdsLock(IgniteUtils.class);
+
+        // Resolve GridGain home via environment variables.
+        String ggHome0 = IgniteSystemProperties.getString(GG_HOME);
+
+        if (!F.isEmpty(ggHome0))
+            return ggHome0;
+
+        String appWorkDir = System.getProperty("user.dir");
+
+        if (appWorkDir != null) {
+            ggHome0 = findProjectHome(new File(appWorkDir));
+
+            if (ggHome0 != null)
+                return ggHome0;
+        }
+
+        URI uri;
+
+        Class<IgniteUtils> cls = IgniteUtils.class;
+
+        try {
+            ProtectionDomain domain = cls.getProtectionDomain();
+
+            // Should not happen, but to make sure our code is not broken.
+            if (domain == null || domain.getCodeSource() == null || domain.getCodeSource().getLocation() == null) {
+                logResolveFailed(cls, null);
+
+                return null;
+            }
+
+            // Resolve path to class-file.
+            uri = domain.getCodeSource().getLocation().toURI();
+
+            // Overcome UNC path problem on Windows (http://www.tomergabel.com/JavaMishandlesUNCPathsOnWindows.aspx)
+            if (isWindows() && uri.getAuthority() != null)
+                uri = new URI(uri.toString().replace("file://", "file:/"));
+        }
+        catch (URISyntaxException | SecurityException e) {
+            logResolveFailed(cls, e);
+
+            return null;
+        }
+
+        return findProjectHome(new File(uri));
+    }
+
+    /**
+     * Tries to find project home starting from specified directory and moving to root.
+     *
+     * @param startDir First directory in search hierarchy.
+     * @return Project home path or {@code null} if it wasn't found.
+     */
+    private static String findProjectHome(File startDir) {
+        for (File cur = startDir.getAbsoluteFile(); cur != null; cur = cur.getParentFile()) {
+            // Check 'cur' is project home directory.
+            if (!new File(cur, "bin").isDirectory() ||
+                !new File(cur, "libs").isDirectory() ||
+                !new File(cur, "config").isDirectory())
+                continue;
+
+            return cur.getPath();
+        }
+
+        return null;
+    }
+
+    /**
+     * @param cls Class.
+     * @param e Exception.
+     */
+    private static void logResolveFailed(Class cls, Exception e) {
+        warn(null, "Failed to resolve GRIDGAIN_HOME automatically for class codebase " +
+            "[class=" + cls + (e == null ? "" : ", e=" + e.getMessage()) + ']');
+    }
+
+    /**
+     * Retrieves {@code GRIDGAIN_HOME} property. The property is retrieved from system
+     * properties or from environment in that order.
+     *
+     * @return {@code GRIDGAIN_HOME} property.
+     */
+    @Nullable public static String getGridGainHome() {
+        GridTuple<String> ggHomeTup = ggHome;
+
+        String ggHome0;
+
+        if (ggHomeTup == null) {
+            synchronized (IgniteUtils.class) {
+                // Double check.
+                ggHomeTup = ggHome;
+
+                if (ggHomeTup == null) {
+                    // Resolve GridGain installation home directory.
+                    ggHome = F.t(ggHome0 = resolveProjectHome());
+
+                    if (ggHome0 != null)
+                        System.setProperty(GG_HOME, ggHome0);
+                }
+                else
+                    ggHome0 = ggHomeTup.get();
+            }
+        }
+        else
+            ggHome0 = ggHomeTup.get();
+
+        return ggHome0;
+    }
+
+    /**
+     * @param path GridGain home. May be {@code null}.
+     */
+    public static void setGridGainHome(@Nullable String path) {
+        GridTuple<String> ggHomeTup = ggHome;
+
+        String ggHome0;
+
+        if (ggHomeTup == null) {
+            synchronized (IgniteUtils.class) {
+                // Double check.
+                ggHomeTup = ggHome;
+
+                if (ggHomeTup == null) {
+                    if (F.isEmpty(path))
+                        System.clearProperty(GG_HOME);
+                    else
+                        System.setProperty(GG_HOME, path);
+
+                    ggHome = F.t(path);
+
+                    return;
+                }
+                else
+                    ggHome0 = ggHomeTup.get();
+            }
+        }
+        else
+            ggHome0 = ggHomeTup.get();
+
+        if (ggHome0 != null && !ggHome0.equals(path))
+            throw new IgniteException("Failed to set GRIDGAIN_HOME after it has been already resolved " +
+                "[ggHome=" + ggHome0 + ", newGgHome=" + path + ']');
+    }
+
+    /**
+     * Gets file associated with path.
+     * <p>
+     * First check if path is relative to {@code GRIDGAIN_HOME}.
+     * If not, check if path is absolute.
+     * If all checks fail, then {@code null} is returned.
+     * <p>
+     * See {@link #getGridGainHome()} for information on how {@code GRIDGAIN_HOME} is retrieved.
+     *
+     * @param path Path to resolve.
+     * @return Resolved path as file, or {@code null} if path cannot be resolved.
+     */
+    @Nullable public static File resolveGridGainPath(String path) {
+        assert path != null;
+
+        /*
+         * 1. Check relative to GRIDGAIN_HOME specified in configuration, if any.
+         */
+
+        String home = getGridGainHome();
+
+        if (home != null) {
+            File file = new File(home, path);
+
+            if (file.exists())
+                return file;
+        }
+
+        /*
+         * 2. Check given path as absolute.
+         */
+
+        File file = new File(path);
+
+        if (file.exists())
+            return file;
+
+        /*
+         * 3. Check development path.
+         */
+
+        if (home != null)
+            file = new File(home, "os/" + path);
+
+        return file.exists() ? file : null;
+    }
+
+    /**
+     * Gets URL representing the path passed in. First the check is made if path is absolute.
+     * If not, then the check is made if path is relative to {@code META-INF} folder in classpath.
+     * If not, then the check is made if path is relative to ${GRIDGAIN_HOME}.
+     * If all checks fail,
+     * then {@code null} is returned, otherwise URL representing path is returned.
+     * <p>
+     * See {@link #getGridGainHome()} for information on how {@code GRIDGAIN_HOME} is retrieved.
+     *
+     * @param path Path to resolve.
+     * @return Resolved path as URL, or {@code null} if path cannot be resolved.
+     * @see #getGridGainHome()
+     */
+    @Nullable public static URL resolveGridGainUrl(String path) {
+        return resolveGridGainUrl(path, true);
+    }
+
+    /**
+     * Gets URL representing the path passed in. First the check is made if path is absolute.
+     * If not, then the check is made if path is relative to {@code META-INF} folder in classpath.
+     * If not, then the check is made if path is relative to ${GRIDGAIN_HOME}.
+     * If all checks fail,
+     * then {@code null} is returned, otherwise URL representing path is returned.
+     * <p>
+     * See {@link #getGridGainHome()} for information on how {@code GRIDGAIN_HOME} is retrieved.
+     *
+     * @param path Path to resolve.
+     * @param metaInf Flag to indicate whether META-INF folder should be checked or class path root.
+     * @return Resolved path as URL, or {@code null} if path cannot be resolved.
+     * @see #getGridGainHome()
+     */
+    @SuppressWarnings({"UnusedCatchParameter"})
+    @Nullable public static URL resolveGridGainUrl(String path, boolean metaInf) {
+        File f = resolveGridGainPath(path);
+
+        if (f == null)
+            f = resolveGridGainPath("os/" + path);
+
+        if (f != null) {
+            try {
+                // Note: we use that method's chain instead of File.getURL() with due
+                // Sun bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6179468
+                return f.toURI().toURL();
+            }
+            catch (MalformedURLException e) {
+                // No-op.
+            }
+        }
+
+        String locPath = (metaInf ? "META-INF/" : "") + path.replaceAll("\\\\", "/");
+
+        return Thread.currentThread().getContextClassLoader().getResource(locPath);
+    }
+
+    /**
+     * Join byte arrays into single one.
+     *
+     * @param bufs list of byte arrays to concatenate.
+     * @return Concatenated byte's array.
+     */
+    public static byte[] join(byte[]... bufs) {
+        int size = 0;
+        for (byte[] buf : bufs) {
+            size += buf.length;
+        }
+
+        byte[] res = new byte[size];
+        int position = 0;
+        for (byte[] buf : bufs) {
+            arrayCopy(buf, 0, res, position, buf.length);
+            position += buf.length;
+        }
+
+        return res;
+    }
+
+    /**
+     * Converts byte array to formatted string. If calling:
+     * <pre name="code" class="java">
+     * ...
+     * byte[] data = {10, 20, 30, 40, 50, 60, 70, 80, 90};
+     *
+     * U.byteArray2String(data, "0x%02X", ",0x%02X")
+     * ...
+     * </pre>
+     * the result will be:
+     * <pre name="code" class="java">
+     * ...
+     * 0x0A, 0x14, 0x1E, 0x28, 0x32, 0x3C, 0x46, 0x50, 0x5A
+     * ...
+     * </pre>
+     *
+     * @param arr Array of byte.
+     * @param hdrFmt C-style string format for the first element.
+     * @param bodyFmt C-style string format for second and following elements, if any.
+     * @return String with converted bytes.
+     */
+    public static String byteArray2String(byte[] arr, String hdrFmt, String bodyFmt) {
+        assert arr != null;
+        assert hdrFmt != null;
+        assert bodyFmt != null;
+
+        SB sb = new SB();
+
+        sb.a('{');
+
+        boolean first = true;
+
+        for (byte b : arr)
+            if (first) {
+                sb.a(String.format(hdrFmt, b));
+
+                first = false;
+            }
+            else
+                sb.a(String.format(bodyFmt, b));
+
+        sb.a('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Converts byte array to hex string.
+     *
+     * @param arr Array of bytes.
+     * @return Hex string.
+     */
+    public static String byteArray2HexString(byte[] arr) {
+        SB sb = new SB(arr.length << 1);
+
+        for (byte b : arr)
+            sb.a(Integer.toHexString(MASK & b >>> 4)).a(Integer.toHexString(MASK & b));
+
+        return sb.toString().toUpperCase();
+    }
+
+    /**
+     * Convert string with hex values to byte array.
+     *
+     * @param hex Hexadecimal string to convert.
+     * @return array of bytes defined as hex in string.
+     * @throws IllegalArgumentException If input character differs from certain hex characters.
+     */
+    public static byte[] hexString2ByteArray(String hex) throws IllegalArgumentException {
+        // If Hex string has odd character length.
+        if (hex.length() % 2 != 0)
+            hex = '0' + hex;
+
+        char[] chars = hex.toCharArray();
+
+        byte[] bytes = new byte[chars.length / 2];
+
+        int byteCnt = 0;
+
+        for (int i = 0; i < chars.length; i += 2) {
+            int newByte = 0;
+
+            newByte |= hexCharToByte(chars[i]);
+
+            newByte <<= 4;
+
+            newByte |= hexCharToByte(chars[i + 1]);
+
+            bytes[byteCnt] = (byte)newByte;
+
+            byteCnt++;
+        }
+
+        return bytes;
+    }
+
+    /**
+     * Return byte value for certain character.
+     *
+     * @param ch Character
+     * @return Byte value.
+     * @throws IllegalArgumentException If input character differ from certain hex characters.
+     */
+    @SuppressWarnings({"UnnecessaryFullyQualifiedName", "fallthrough"})
+    private static byte hexCharToByte(char ch) throws IllegalArgumentException {
+        switch (ch) {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                return (byte)(ch - '0');
+
+            case 'a':
+            case 'A':
+                return 0xa;
+
+            case 'b':
+            case 'B':
+                return 0xb;
+
+            case 'c':
+            case 'C':
+                return 0xc;
+
+            case 'd':
+            case 'D':
+                return 0xd;
+
+            case 'e':
+            case 'E':
+                return 0xe;
+
+            case 'f':
+            case 'F':
+                return 0xf;
+
+            default:
+                throw new IllegalArgumentException("Hex decoding wrong input character [character=" + ch + ']');
+        }
+    }
+
+    /**
+     * Converts primitive double to byte array.
+     *
+     * @param d Double to convert.
+     * @return Byte array.
+     */
+    public static byte[] doubleToBytes(double d) {
+        return longToBytes(Double.doubleToLongBits(d));
+    }
+
+    /**
+     * Converts primitive {@code double} type to byte array and stores
+     * it in the specified byte array.
+     *
+     * @param d Double to convert.
+     * @param bytes Array of bytes.
+     * @param off Offset.
+     * @return New offset.
+     */
+    public static int doubleToBytes(double d, byte[] bytes, int off) {
+        return longToBytes(Double.doubleToLongBits(d), bytes, off);
+    }
+
+    /**
+     * Converts primitive float to byte array.
+     *
+     * @param f Float to convert.
+     * @return Array of bytes.
+     */
+    public static byte[] floatToBytes(float f) {
+        return intToBytes(Float.floatToIntBits(f));
+    }
+
+    /**
+     * Converts primitive float to byte array.
+     *
+     * @param f Float to convert.
+     * @param bytes Array of bytes.
+     * @param off Offset.
+     * @return New offset.
+     */
+    public static int floatToBytes(float f, byte[] bytes, int off) {
+        return intToBytes(Float.floatToIntBits(f), bytes, off);
+    }
+
+    /**
+     * Converts primitive {@code long} type to byte array.
+     *
+     * @param l Long value.
+     * @return Array of bytes.
+     */
+    public static byte[] longToBytes(long l) {
+        return GridClientByteUtils.longToBytes(l);
+    }
+
+    /**
+     * Converts primitive {@code long} type to byte array and stores it in specified
+     * byte array.
+     *
+     * @param l Long value.
+     * @param bytes Array of bytes.
+     * @param off Offset in {@code bytes} array.
+     * @return Number o

<TRUNCATED>