http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/ac05bb9c/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java index 0000000,fedf24b..013367f mode 000000,100644..100644 --- 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 @@@ -1,0 -1,9141 +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); ++ public static final byte[] IGNITE_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); ++ public static final String IGNITE_LOG_DIR = System.getenv(IgniteSystemProperties.IGNITE_LOG_DIR); + + /** GridGain Work Directory. */ - public static final String GRIDGAIN_WORK_DIR = System.getenv(GG_WORK_DIR); ++ public static final String IGNITE_WORK_DIR = System.getenv(IgniteSystemProperties.IGNITE_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))) { ++ if (Boolean.parseBoolean(System.getProperty(IGNITE_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)); ++ return "true".equalsIgnoreCase(System.getProperty(IGNITE_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); ++ String sysLocHost = IgniteSystemProperties.getString(IGNITE_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 ".*://(.*:.*)@.*" + * 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); ++ String ggHome0 = IgniteSystemProperties.getString(IGNITE_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 " + ++ warn(null, "Failed to resolve IGNITE_HOME automatically for class codebase " + + "[class=" + cls + (e == null ? "" : ", e=" + e.getMessage()) + ']'); + } + + /** - * Retrieves {@code GRIDGAIN_HOME} property. The property is retrieved from system ++ * Retrieves {@code IGNITE_HOME} property. The property is retrieved from system + * properties or from environment in that order. + * - * @return {@code GRIDGAIN_HOME} property. ++ * @return {@code IGNITE_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); ++ System.setProperty(IGNITE_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); ++ System.clearProperty(IGNITE_HOME); + else - System.setProperty(GG_HOME, path); ++ System.setProperty(IGNITE_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 " + ++ throw new IgniteException("Failed to set IGNITE_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}. ++ * First check if path is relative to {@code IGNITE_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. ++ * See {@link #getGridGainHome()} for information on how {@code IGNITE_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. ++ * 1. Check relative to IGNITE_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 not, then the check is made if path is relative to ${IGNITE_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. ++ * See {@link #getGridGainHome()} for information on how {@code IGNITE_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 not, then the check is made if path is relative to ${IGNITE_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. ++ * See {@link #getGridGainHome()} for information on how {@code IGNITE_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
<TRUNCATED>