/*
 * 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 io.arivera.oss.embedded.rabbitmq.apache.commons.lang3;

import java.io.File;

/**
 *

 * Helpers for {@code java.lang.System}.
 *

 *

 * If a system property cannot be read due to security restrictions, the corresponding field in this class will be set
 * to {@code null} and a message will be written to {@code System.err}.
 *

 *

 * #ThreadSafe#
 *

 *
 * @since 1.0
 */
@SuppressWarnings("all")
public class SystemUtils {

    /**
     * The prefix String for all Windows OS.
     */
    private static final String OS_NAME_WINDOWS_PREFIX = "Windows";

    // System property constants
    // -----------------------------------------------------------------------
    // These MUST be declared first. Other constants depend on this.

    /**
     * The System property key for the user home directory.
     */
    private static final String USER_HOME_KEY = "user.home";

    /**
     * The System property key for the user directory.
     */
    private static final String USER_DIR_KEY = "user.dir";

    /**
     * The System property key for the Java IO temporary directory.
     */
    private static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir";

    /**
     * The System property key for the Java home directory.
     */
    private static final String JAVA_HOME_KEY = "java.home";

    /**
     *

     * The {@code awt.toolkit} System Property.
     *

     *

     * Holds a class name, on Windows XP this is {@code sun.awt.windows.WToolkit}.
     *

     *

     * On platforms without a GUI, this value is {@code null}.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.1
     */
    public static final String AWT_TOOLKIT = getSystemProperty("awt.toolkit");

    /**
     *

     * The {@code file.encoding} System Property.
     *

     *

     * File encoding, such as {@code Cp1252}.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.0
     * @since Java 1.2
     */
    public static final String FILE_ENCODING = getSystemProperty("file.encoding");

    /**
     *

     * The {@code file.separator} System Property.
     * The file separator is:
     *

     *

     *
{@code "/"} on UNIX

     *
{@code "\"} on Windows.

     *

     *
     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @deprecated Use {@link File#separator}, since it is guaranteed to be a
     *             string containing a single character and it does not require a privilege check.
     * @since Java 1.1
     */
    @Deprecated
    public static final String FILE_SEPARATOR = getSystemProperty("file.separator");

    /**
     *

     * The {@code java.awt.fonts} System Property.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.1
     */
    public static final String JAVA_AWT_FONTS = getSystemProperty("java.awt.fonts");

    /**
     *

     * The {@code java.awt.graphicsenv} System Property.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.1
     */
    public static final String JAVA_AWT_GRAPHICSENV = getSystemProperty("java.awt.graphicsenv");

    /**
     *

     * The {@code java.awt.headless} System Property. The value of this property is the String {@code "true"} or
     * {@code "false"}.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @see #isJavaAwtHeadless()
     * @since 2.1
     * @since Java 1.4
     */
    public static final String JAVA_AWT_HEADLESS = getSystemProperty("java.awt.headless");

    /**
     *

     * The {@code java.awt.printerjob} System Property.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.1
     */
    public static final String JAVA_AWT_PRINTERJOB = getSystemProperty("java.awt.printerjob");

    /**
     *

     * The {@code java.class.path} System Property. Java class path.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String JAVA_CLASS_PATH = getSystemProperty("java.class.path");

    /**
     *

     * The {@code java.class.version} System Property. Java class format version number.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String JAVA_CLASS_VERSION = getSystemProperty("java.class.version");

    /**
     *

     * The {@code java.compiler} System Property. Name of JIT compiler to use. First in JDK version 1.2. Not used in Sun
     * JDKs after 1.2.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2. Not used in Sun versions after 1.2.
     */
    public static final String JAVA_COMPILER = getSystemProperty("java.compiler");

    /**
     *

     * The {@code java.endorsed.dirs} System Property. Path of endorsed directory or directories.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.4
     */
    public static final String JAVA_ENDORSED_DIRS = getSystemProperty("java.endorsed.dirs");

    /**
     *

     * The {@code java.ext.dirs} System Property. Path of extension directory or directories.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.3
     */
    public static final String JAVA_EXT_DIRS = getSystemProperty("java.ext.dirs");

    /**
     *

     * The {@code java.home} System Property. Java installation directory.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String JAVA_HOME = getSystemProperty(JAVA_HOME_KEY);

    /**
     *

     * The {@code java.io.tmpdir} System Property. Default temp file path.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_IO_TMPDIR = getSystemProperty(JAVA_IO_TMPDIR_KEY);

    /**
     *

     * The {@code java.library.path} System Property. List of paths to search when loading libraries.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_LIBRARY_PATH = getSystemProperty("java.library.path");

    /**
     *

     * The {@code java.runtime.name} System Property. Java Runtime Environment name.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.0
     * @since Java 1.3
     */
    public static final String JAVA_RUNTIME_NAME = getSystemProperty("java.runtime.name");

    /**
     *

     * The {@code java.runtime.version} System Property. Java Runtime Environment version.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.0
     * @since Java 1.3
     */
    public static final String JAVA_RUNTIME_VERSION = getSystemProperty("java.runtime.version");

    /**
     *

     * The {@code java.specification.name} System Property. Java Runtime Environment specification name.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_SPECIFICATION_NAME = getSystemProperty("java.specification.name");

    /**
     *

     * The {@code java.specification.vendor} System Property. Java Runtime Environment specification vendor.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_SPECIFICATION_VENDOR = getSystemProperty("java.specification.vendor");

    /**
     *

     * The {@code java.specification.version} System Property. Java Runtime Environment specification version.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.3
     */
    public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version");
    private static final JavaVersion JAVA_SPECIFICATION_VERSION_AS_ENUM = JavaVersion.get(JAVA_SPECIFICATION_VERSION);

    /**
     *

     * The {@code java.util.prefs.PreferencesFactory} System Property. A class name.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.1
     * @since Java 1.4
     */
    public static final String JAVA_UTIL_PREFS_PREFERENCES_FACTORY =
        getSystemProperty("java.util.prefs.PreferencesFactory");

    /**
     *

     * The {@code java.vendor} System Property. Java vendor-specific string.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String JAVA_VENDOR = getSystemProperty("java.vendor");

    /**
     *

     * The {@code java.vendor.url} System Property. Java vendor URL.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String JAVA_VENDOR_URL = getSystemProperty("java.vendor.url");

    /**
     *

     * The {@code java.version} System Property. Java version number.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String JAVA_VERSION = getSystemProperty("java.version");

    /**
     *

     * The {@code java.vm.info} System Property. Java Virtual Machine implementation info.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.0
     * @since Java 1.2
     */
    public static final String JAVA_VM_INFO = getSystemProperty("java.vm.info");

    /**
     *

     * The {@code java.vm.name} System Property. Java Virtual Machine implementation name.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_VM_NAME = getSystemProperty("java.vm.name");

    /**
     *

     * The {@code java.vm.specification.name} System Property. Java Virtual Machine specification name.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_VM_SPECIFICATION_NAME = getSystemProperty("java.vm.specification.name");

    /**
     *

     * The {@code java.vm.specification.vendor} System Property. Java Virtual Machine specification vendor.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_VM_SPECIFICATION_VENDOR = getSystemProperty("java.vm.specification.vendor");

    /**
     *

     * The {@code java.vm.specification.version} System Property. Java Virtual Machine specification version.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_VM_SPECIFICATION_VERSION = getSystemProperty("java.vm.specification.version");

    /**
     *

     * The {@code java.vm.vendor} System Property. Java Virtual Machine implementation vendor.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_VM_VENDOR = getSystemProperty("java.vm.vendor");

    /**
     *

     * The {@code java.vm.version} System Property. Java Virtual Machine implementation version.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.2
     */
    public static final String JAVA_VM_VERSION = getSystemProperty("java.vm.version");

    /**
     *

     * The {@code line.separator} System Property. Line separator ("\n" on UNIX).
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String LINE_SEPARATOR = getSystemProperty("line.separator");

    /**
     *

     * The {@code os.arch} System Property. Operating system architecture.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String OS_ARCH = getSystemProperty("os.arch");

    /**
     *

     * The {@code os.name} System Property. Operating system name.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String OS_NAME = getSystemProperty("os.name");

    /**
     *

     * The {@code os.version} System Property. Operating system version.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String OS_VERSION = getSystemProperty("os.version");

    /**
     *

     * The {@code path.separator} System Property. Path separator (":" on UNIX).
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @deprecated Use {@link File#pathSeparator}, since it is guaranteed to be a
     *             string containing a single character and it does not require a privilege check.
     * @since Java 1.1
     */
    @Deprecated
    public static final String PATH_SEPARATOR = getSystemProperty("path.separator");

    /**
     *

     * The {@code user.country} or {@code user.region} System Property. User's country code, such as {@code GB}. First
     * in Java version 1.2 as {@code user.region}. Renamed to {@code user.country} in 1.4
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.0
     * @since Java 1.2
     */
    public static final String USER_COUNTRY = getSystemProperty("user.country") == null ?
        getSystemProperty("user.region") : getSystemProperty("user.country");

    /**
     *

     * The {@code user.dir} System Property. User's current working directory.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String USER_DIR = getSystemProperty(USER_DIR_KEY);

    /**
     *

     * The {@code user.home} System Property. User's home directory.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String USER_HOME = getSystemProperty(USER_HOME_KEY);

    /**
     *

     * The {@code user.language} System Property. User's language code, such as {@code "en"}.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.0
     * @since Java 1.2
     */
    public static final String USER_LANGUAGE = getSystemProperty("user.language");

    /**
     *

     * The {@code user.name} System Property. User's account name.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since Java 1.1
     */
    public static final String USER_NAME = getSystemProperty("user.name");

    /**
     *

     * The {@code user.timezone} System Property. For example: {@code "America/Los_Angeles"}.
     *

     *

     * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
     * not exist.
     *

     *

     * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
     * sync with that System property.
     *

     *
     * @since 2.1
     */
    public static final String USER_TIMEZONE = getSystemProperty("user.timezone");

    // Java version checks
    // -----------------------------------------------------------------------
    // These MUST be declared after those above as they depend on the
    // values being set up

    /**
     *

     * Is {@code true} if this is Java version 1.1 (also 1.1.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     */
    public static final boolean IS_JAVA_1_1 = getJavaVersionMatches("1.1");

    /**
     *

     * Is {@code true} if this is Java version 1.2 (also 1.2.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     */
    public static final boolean IS_JAVA_1_2 = getJavaVersionMatches("1.2");

    /**
     *

     * Is {@code true} if this is Java version 1.3 (also 1.3.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     */
    public static final boolean IS_JAVA_1_3 = getJavaVersionMatches("1.3");

    /**
     *

     * Is {@code true} if this is Java version 1.4 (also 1.4.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     */
    public static final boolean IS_JAVA_1_4 = getJavaVersionMatches("1.4");

    /**
     *

     * Is {@code true} if this is Java version 1.5 (also 1.5.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     */
    public static final boolean IS_JAVA_1_5 = getJavaVersionMatches("1.5");

    /**
     *

     * Is {@code true} if this is Java version 1.6 (also 1.6.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     */
    public static final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6");

    /**
     *

     * Is {@code true} if this is Java version 1.7 (also 1.7.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     *
     * @since 3.0
     */
    public static final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7");

    /**
     *

     * Is {@code true} if this is Java version 1.8 (also 1.8.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     *
     * @since 3.3.2
     */
    public static final boolean IS_JAVA_1_8 = getJavaVersionMatches("1.8");

    /**
     *

     * Is {@code true} if this is Java version 1.9 (also 1.9.x versions).
     *

     *

     * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_JAVA_1_9 = getJavaVersionMatches("1.9");

    // Operating system checks
    // -----------------------------------------------------------------------
    // These MUST be declared after those above as they depend on the
    // values being set up
    // OS names from http://www.vamphq.com/os.html
    // Selected ones included - please advise dev@commons.apache.org
    // if you want another added or a mistake corrected

    /**
     *

     * Is {@code true} if this is AIX.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_AIX = getOSMatchesName("AIX");

    /**
     *

     * Is {@code true} if this is HP-UX.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_HP_UX = getOSMatchesName("HP-UX");

    /**
     *

     * Is {@code true} if this is IBM OS/400.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.3
     */
    public static final boolean IS_OS_400 = getOSMatchesName("OS/400");

    /**
     *

     * Is {@code true} if this is Irix.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_IRIX = getOSMatchesName("Irix");

    /**
     *

     * Is {@code true} if this is Linux.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_LINUX = getOSMatchesName("Linux") || getOSMatchesName("LINUX");

    /**
     *

     * Is {@code true} if this is Mac.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_MAC = getOSMatchesName("Mac");

    /**
     *

     * Is {@code true} if this is Mac.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_MAC_OSX = getOSMatchesName("Mac OS X");

    /**
     *

     * Is {@code true} if this is Mac OS X Cheetah.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_CHEETAH = getOSMatches("Mac OS X", "10.0");

    /**
     *

     * Is {@code true} if this is Mac OS X Puma.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_PUMA = getOSMatches("Mac OS X", "10.1");

    /**
     *

     * Is {@code true} if this is Mac OS X Jaguar.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_JAGUAR = getOSMatches("Mac OS X", "10.2");

    /**
     *

     * Is {@code true} if this is Mac OS X Panther.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_PANTHER = getOSMatches("Mac OS X", "10.3");

    /**
     *

     * Is {@code true} if this is Mac OS X Tiger.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_TIGER = getOSMatches("Mac OS X", "10.4");

    /**
     *

     * Is {@code true} if this is Mac OS X Leopard.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_LEOPARD = getOSMatches("Mac OS X", "10.5");

    /**
     *

     * Is {@code true} if this is Mac OS X Snow Leopard.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_SNOW_LEOPARD = getOSMatches("Mac OS X", "10.6");

    /**
     *

     * Is {@code true} if this is Mac OS X Lion.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_LION = getOSMatches("Mac OS X", "10.7");

    /**
     *

     * Is {@code true} if this is Mac OS X Mountain Lion.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_MOUNTAIN_LION = getOSMatches("Mac OS X", "10.8");

    /**
     *

     * Is {@code true} if this is Mac OS X Mavericks.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_MAVERICKS = getOSMatches("Mac OS X", "10.9");

    /**
     *

     * Is {@code true} if this is Mac OS X Yosemite.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_MAC_OSX_YOSEMITE = getOSMatches("Mac OS X", "10.10");

    /**
     *

     * Is {@code true} if this is Mac OS X El Capitan.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.5
     */
    public static final boolean IS_OS_MAC_OSX_EL_CAPITAN = getOSMatches("Mac OS X", "10.11");

    /**
     *

     * Is {@code true} if this is FreeBSD.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.1
     */
    public static final boolean IS_OS_FREE_BSD = getOSMatchesName("FreeBSD");

    /**
     *

     * Is {@code true} if this is OpenBSD.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.1
     */
    public static final boolean IS_OS_OPEN_BSD = getOSMatchesName("OpenBSD");

    /**
     *

     * Is {@code true} if this is NetBSD.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.1
     */
    public static final boolean IS_OS_NET_BSD = getOSMatchesName("NetBSD");

    /**
     *

     * Is {@code true} if this is OS/2.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_OS2 = getOSMatchesName("OS/2");

    /**
     *

     * Is {@code true} if this is Solaris.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_SOLARIS = getOSMatchesName("Solaris");

    /**
     *

     * Is {@code true} if this is SunOS.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_SUN_OS = getOSMatchesName("SunOS");

    /**
     *

     * Is {@code true} if this is a UNIX like system, as in any of AIX, HP-UX, Irix, Linux, MacOSX, Solaris or SUN OS.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.1
     */
    public static final boolean IS_OS_UNIX = IS_OS_AIX || IS_OS_HP_UX || IS_OS_IRIX || IS_OS_LINUX || IS_OS_MAC_OSX
        || IS_OS_SOLARIS || IS_OS_SUN_OS || IS_OS_FREE_BSD || IS_OS_OPEN_BSD || IS_OS_NET_BSD;

    /**
     *

     * Is {@code true} if this is Windows.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS = getOSMatchesName(OS_NAME_WINDOWS_PREFIX);

    /**
     *

     * Is {@code true} if this is Windows 2000.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_2000 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " 2000");

    /**
     *

     * Is {@code true} if this is Windows 2003.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.1
     */
    public static final boolean IS_OS_WINDOWS_2003 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " 2003");

    /**
     *

     * Is {@code true} if this is Windows Server 2008.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.1
     */
    public static final boolean IS_OS_WINDOWS_2008 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " Server 2008");

    /**
     *

     * Is {@code true} if this is Windows Server 2012.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.4
     */
    public static final boolean IS_OS_WINDOWS_2012 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " Server 2012");

    /**
     *

     * Is {@code true} if this is Windows 95.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_95 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " 95");

    /**
     *

     * Is {@code true} if this is Windows 98.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_98 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " 98");

    /**
     *

     * Is {@code true} if this is Windows ME.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_ME = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " Me");

    /**
     *

     * Is {@code true} if this is Windows NT.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_NT = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " NT");

    /**
     *

     * Is {@code true} if this is Windows XP.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_XP = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " XP");

    // -----------------------------------------------------------------------
    /**
     *

     * Is {@code true} if this is Windows Vista.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 2.4
     */
    public static final boolean IS_OS_WINDOWS_VISTA = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " Vista");

    /**
     *

     * Is {@code true} if this is Windows 7.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.0
     */
    public static final boolean IS_OS_WINDOWS_7 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " 7");

    /**
     *

     * Is {@code true} if this is Windows 8.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.2
     */
    public static final boolean IS_OS_WINDOWS_8 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " 8");

    /**
     *

     * Is {@code true} if this is Windows 10.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.5
     */
    public static final boolean IS_OS_WINDOWS_10 = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " 10");

    /**
     *

     * Is {@code true} if this is z/OS.
     *

     *

     * The field will return {@code false} if {@code OS_NAME} is {@code null}.
     *

     *
     * @since 3.5
     */
    // Values on a z/OS system I tested (Gary Gregory - 2016-03-12)
    // os.arch = s390x
    // os.encoding = ISO8859_1
    // os.name = z/OS
    // os.version = 02.02.00
    public static final boolean IS_OS_ZOS = getOSMatchesName("z/OS");

    /**
     *

     * Gets the Java home directory as a {@code File}.
     *

     *
     * @return a directory
     * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
     * access to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getJavaHome() {
        return new File(System.getProperty(JAVA_HOME_KEY));
    }

    /**
     *

     * Gets the Java IO temporary directory as a {@code File}.
     *

     *
     * @return a directory
     * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
     * access to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getJavaIoTmpDir() {
        return new File(System.getProperty(JAVA_IO_TMPDIR_KEY));
    }

    /**
     *

     * Decides if the Java version matches.
     *

     *
     * @param versionPrefix the prefix for the java version
     * @return true if matches, or false if not or can't determine
     */
    private static boolean getJavaVersionMatches(final String versionPrefix) {
        return isJavaVersionMatch(JAVA_SPECIFICATION_VERSION, versionPrefix);
    }

    /**
     * Decides if the operating system matches.
     *
     * @param osNamePrefix the prefix for the os name
     * @param osVersionPrefix the prefix for the version
     * @return true if matches, or false if not or can't determine
     */
    private static boolean getOSMatches(final String osNamePrefix, final String osVersionPrefix) {
        return isOSMatch(OS_NAME, OS_VERSION, osNamePrefix, osVersionPrefix);
    }

    /**
     * Decides if the operating system matches.
     *
     * @param osNamePrefix the prefix for the os name
     * @return true if matches, or false if not or can't determine
     */
    private static boolean getOSMatchesName(final String osNamePrefix) {
        return isOSNameMatch(OS_NAME, osNamePrefix);
    }

    // -----------------------------------------------------------------------
    /**
     *

     * Gets a System property, defaulting to {@code null} if the property cannot be read.
     *

     *

     * If a {@code SecurityException} is caught, the return value is {@code null} and a message is written to
     * {@code System.err}.
     *

     *
     * @param property the system property name
     * @return the system property value or {@code null} if a security problem occurs
     */
    private static String getSystemProperty(final String property) {
        try {
            return System.getProperty(property);
        } catch (final SecurityException ex) {
            // we are not allowed to look at this property
            System.err.println("Caught a SecurityException reading the system property '" + property
                + "'; the SystemUtils property value will default to null.");
            return null;
        }
    }

    /**
     *

     * Gets the user directory as a {@code File}.
     *

     *
     * @return a directory
     * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
     * access to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getUserDir() {
        return new File(System.getProperty(USER_DIR_KEY));
    }

    /**
     *

     * Gets the user home directory as a {@code File}.
     *

     *
     * @return a directory
     * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
     * access to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getUserHome() {
        return new File(System.getProperty(USER_HOME_KEY));
    }

    /**
     * Returns whether the {@link #JAVA_AWT_HEADLESS} value is {@code true}.
     *
     * @return {@code true} if {@code JAVA_AWT_HEADLESS} is {@code "true"}, {@code false} otherwise.
     * @see #JAVA_AWT_HEADLESS
     * @since 2.1
     * @since Java 1.4
     */
    public static boolean isJavaAwtHeadless() {
        return JAVA_AWT_HEADLESS != null ? JAVA_AWT_HEADLESS.equals(Boolean.TRUE.toString()) : false;
    }

    /**
     *

     * Is the Java version at least the requested version.
     *

     *

     * Example input:
     *

     *

     *
{@code 1.2f} to test for Java 1.2

     *
{@code 1.31f} to test for Java 1.3.1

     *

     *
     * @param requiredVersion the required version, for example 1.31f
     * @return {@code true} if the actual version is equal or greater than the required version
     */
    public static boolean isJavaVersionAtLeast(final JavaVersion requiredVersion) {
        return JAVA_SPECIFICATION_VERSION_AS_ENUM.atLeast(requiredVersion);
    }

    /**
     *

     * Decides if the Java version matches.
     *

     *

     * This method is package private instead of private to support unit test invocation.
     *

     *
     * @param version the actual Java version
     * @param versionPrefix the prefix for the expected Java version
     * @return true if matches, or false if not or can't determine
     */
    static boolean isJavaVersionMatch(final String version, final String versionPrefix) {
        if (version == null) {
            return false;
        }
        return version.startsWith(versionPrefix);
    }

    /**
     * Decides if the operating system matches.
     *

     * This method is package private instead of private to support unit test invocation.
     *

     *
     * @param osName the actual OS name
     * @param osVersion the actual OS version
     * @param osNamePrefix the prefix for the expected OS name
     * @param osVersionPrefix the prefix for the expected OS version
     * @return true if matches, or false if not or can't determine
     */
    static boolean isOSMatch(final String osName, final String osVersion, final String osNamePrefix, final String osVersionPrefix) {
        if (osName == null || osVersion == null) {
            return false;
        }
        return isOSNameMatch(osName, osNamePrefix) && isOSVersionMatch(osVersion, osVersionPrefix);
    }

    /**
     * Decides if the operating system matches.
     *

     * This method is package private instead of private to support unit test invocation.
     *

     *
     * @param osName the actual OS name
     * @param osNamePrefix the prefix for the expected OS name
     * @return true if matches, or false if not or can't determine
     */
    static boolean isOSNameMatch(final String osName, final String osNamePrefix) {
        if (osName == null) {
            return false;
        }
        return osName.startsWith(osNamePrefix);
    }

    /**
     * Decides if the operating system version matches.
     *

     * This method is package private instead of private to support unit test invocation.
     *

     *
     * @param osVersion the actual OS version
     * @param osVersionPrefix the prefix for the expected OS version
     * @return true if matches, or false if not or can't determine
     */
    static boolean isOSVersionMatch(final String osVersion, final String osVersionPrefix) {
        if (osVersion == null || osVersion.trim().isEmpty()) {
            return false;
        }
        // Compare parts of the version string instead of using String.startsWith(String) because otherwise
        // osVersionPrefix 10.1 would also match osVersion 10.10
        String[] versionPrefixParts = osVersionPrefix.split("\\.");
        String[] versionParts = osVersion.split("\\.");
        for (int i = 0; i < Math.min(versionPrefixParts.length, versionParts.length); i++) {
            if (!versionPrefixParts[i].equals(versionParts[i])) {
                return false;
            }
        }
        return true;
    }

    // -----------------------------------------------------------------------
    /**
     *

     * SystemUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
     * {@code SystemUtils.FILE_SEPARATOR}.
     *

     *

     * This constructor is public to permit tools that require a JavaBean instance to operate.
     *

     */
    public SystemUtils() {
        super();
    }

}


package io.arivera.oss.embedded.rabbitmq;

import io.arivera.oss.embedded.rabbitmq.apache.commons.lang3.SystemUtils;
import io.arivera.oss.embedded.rabbitmq.bin.RabbitMqCommand;
import io.arivera.oss.embedded.rabbitmq.bin.RabbitMqCtl;
import io.arivera.oss.embedded.rabbitmq.bin.RabbitMqPlugins;
import io.arivera.oss.embedded.rabbitmq.bin.RabbitMqServer;
import io.arivera.oss.embedded.rabbitmq.util.OperatingSystem;
import io.arivera.oss.embedded.rabbitmq.util.RandomPortSupplier;

import java.io.File;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Immutable configuration used to control all interactions with RabbitMQ broker and respective commands.
 *

 * Use {@link EmbeddedRabbitMqConfig.Builder} to construct a new instance.
 *
 * Once you have created an instance, you can use it to create a new instance of {@link EmbeddedRabbitMq}
 * or {@link RabbitMqCommand} or any of the helpers, like {@link RabbitMqPlugins}.
 *
 * @see EmbeddedRabbitMqConfig.Builder
 * @see EmbeddedRabbitMq
 * @see RabbitMqCommand
 * @see RabbitMqCtl
 * @see RabbitMqPlugins
 * @see RabbitMqServer
 */
public class EmbeddedRabbitMqConfig {

  private final Version version;

  private final URL downloadSource;
  private final File downloadTarget;
  private final File extractionFolder;
  private final File appFolder;

  private final long downloadReadTimeoutInMillis;
  private final long downloadConnectionTimeoutInMillis;
  private final long defaultRabbitMqCtlTimeoutInMillis;
  private final long rabbitMqServerInitializationTimeoutInMillis;
  private final long erlangCheckTimeoutInMillis;

  private final boolean shouldCacheDownload;
  private final boolean deleteCachedFileOnErrors;

  private final Map<String, String> envVars;
  private final RabbitMqCommand.ProcessExecutorFactory processExecutorFactory;
  private final Proxy downloadProxy;

  protected EmbeddedRabbitMqConfig(Version version,
                                   URL downloadSource,
                                   File downloadTarget,
                                   File extractionFolder,
                                   File appFolder,
                                   long downloadReadTimeoutInMillis,
                                   long downloadConnectionTimeoutInMillis,
                                   long defaultRabbitMqCtlTimeoutInMillis,
                                   long rabbitMqServerInitializationTimeoutInMillis,
                                   long erlangCheckTimeoutInMillis,
                                   boolean cacheDownload, boolean deleteCachedFile,
                                   Map<String, String> envVars,
                                   RabbitMqCommand.ProcessExecutorFactory processExecutorFactory,
                                   Proxy downloadProxy ) {
    this.version = version;
    this.downloadSource = downloadSource;
    this.downloadTarget = downloadTarget;
    this.extractionFolder = extractionFolder;
    this.appFolder = appFolder;
    this.downloadReadTimeoutInMillis = downloadReadTimeoutInMillis;
    this.downloadConnectionTimeoutInMillis = downloadConnectionTimeoutInMillis;
    this.defaultRabbitMqCtlTimeoutInMillis = defaultRabbitMqCtlTimeoutInMillis;
    this.rabbitMqServerInitializationTimeoutInMillis = rabbitMqServerInitializationTimeoutInMillis;
    this.erlangCheckTimeoutInMillis = erlangCheckTimeoutInMillis;
    this.shouldCacheDownload = cacheDownload;
    this.deleteCachedFileOnErrors = deleteCachedFile;
    this.envVars = envVars;
    this.processExecutorFactory = processExecutorFactory;
    this.downloadProxy = downloadProxy;
  }

  public long getDownloadReadTimeoutInMillis() {
    return downloadReadTimeoutInMillis;
  }

  public long getDownloadConnectionTimeoutInMillis() {
    return downloadConnectionTimeoutInMillis;
  }

  public long getDefaultRabbitMqCtlTimeoutInMillis() {
    return defaultRabbitMqCtlTimeoutInMillis;
  }

  public long getRabbitMqServerInitializationTimeoutInMillis() {
    return rabbitMqServerInitializationTimeoutInMillis;
  }

  public long getErlangCheckTimeoutInMillis() {
    return erlangCheckTimeoutInMillis;
  }

  public URL getDownloadSource() {
    return downloadSource;
  }

  public File getDownloadTarget() {
    return downloadTarget;
  }

  public File getExtractionFolder() {
    return extractionFolder;
  }

  public boolean shouldCachedDownload() {
    return shouldCacheDownload;
  }

  public boolean shouldDeleteCachedFileOnErrors() {
    return deleteCachedFileOnErrors;
  }

  public File getAppFolder() {
    return appFolder;
  }

  public Map<String, String> getEnvVars() {
    return envVars;
  }

  public RabbitMqCommand.ProcessExecutorFactory getProcessExecutorFactory() {
    return processExecutorFactory;
  }

  public Version getVersion() {
    return version;
  }

  /**
   * Returns the RabbitMQ node port as defined by the {@link #envVars} or the default port if not defined.
   *

   * This method will return the correct port even if the {@link Builder#randomPort()} method was used.
   */
  public int getRabbitMqPort() {
    String portValue = this.envVars.get(RabbitMqEnvVar.NODE_PORT.getEnvVarName());
    if (portValue == null) {
      return RabbitMqEnvVar.DEFAULT_NODE_PORT;
    } else {
      return Integer.parseInt(portValue);
    }
  }

  public Proxy getDownloadProxy() {
    return downloadProxy;
  }

  /**
   * A user-friendly way to create a new {@link EmbeddedRabbitMqConfig} instance.
   *

   * Example use:
   *

   * {@code
   * EmbeddedRabbitMqConfig config = new EmbeddedRabbitMqConfig.Builder()
   *      .version(...);
   *      .downloadFrom(...)
   *      ...
   *      .build();
   * }
   *

   *
   * @see EmbeddedRabbitMqConfig
   */
  public static class Builder {

    public static final String DOWNLOAD_FOLDER = ".embeddedrabbitmq";
    private long downloadReadTimeoutInMillis;
    private long downloadConnectionTimeoutInMillis;
    private long defaultRabbitMqCtlTimeoutInMillis;
    private long rabbitMqServerInitializationTimeoutInMillis;
    private long erlangCheckTimeoutInMillis;
    private File downloadFolder;
    private File downloadTarget;
    private File extractionFolder;
    private boolean cacheDownload;
    private boolean deleteCachedFile;
    private Version version;
    private Map<String, String> envVars;
    private ArtifactRepository artifactRepository;
    private RabbitMqCommand.ProcessExecutorFactory processExecutorFactory;
    private Proxy downloadProxy = null;

    /**
     * Creates a new instance of the Configuration Builder.
     *

     * This class uses the Builder pattern where you can chain several methods and invoke {@link #build()} at the end.
     */
    public Builder() {
      this.downloadConnectionTimeoutInMillis = TimeUnit.SECONDS.toMillis(2);
      this.downloadReadTimeoutInMillis = TimeUnit.SECONDS.toMillis(3);
      this.defaultRabbitMqCtlTimeoutInMillis = TimeUnit.SECONDS.toMillis(2);
      this.rabbitMqServerInitializationTimeoutInMillis = TimeUnit.SECONDS.toMillis(3);
      this.erlangCheckTimeoutInMillis = TimeUnit.SECONDS.toMillis(1);
      this.cacheDownload = true;
      this.deleteCachedFile = true;
      this.downloadFolder = new File(System.getProperty("user.home"), DOWNLOAD_FOLDER);
      this.artifactRepository = OfficialArtifactRepository.GITHUB;
      this.envVars = new HashMap<>();
      this.processExecutorFactory = new RabbitMqCommand.ProcessExecutorFactory();
    }

    @Beta
    public Builder downloadReadTimeoutInMillis(long downloadReadTimeoutInMillis) {
      this.downloadReadTimeoutInMillis = downloadReadTimeoutInMillis;
      return this;
    }

    @Beta
    public Builder downloadConnectionTimeoutInMillis(long downloadConnectionTimeoutInMillis) {
      this.downloadConnectionTimeoutInMillis = downloadConnectionTimeoutInMillis;
      return this;
    }

    @Beta
    public Builder defaultRabbitMqCtlTimeoutInMillis(long defaultRabbitMqCtlTimeoutInMillis) {
      this.defaultRabbitMqCtlTimeoutInMillis = defaultRabbitMqCtlTimeoutInMillis;
      return this;
    }

    @Beta
    public Builder rabbitMqServerInitializationTimeoutInMillis(long rabbitMqServerInitializationTimeoutInMillis) {
      this.rabbitMqServerInitializationTimeoutInMillis = rabbitMqServerInitializationTimeoutInMillis;
      return this;
    }

    @Beta
    public Builder erlangCheckTimeoutInMillis(long erlangCheckTimeoutInMillis) {
      this.erlangCheckTimeoutInMillis = erlangCheckTimeoutInMillis;
      return this;
    }

    /**
     * Defines where the artifact should be downloaded from and characteristics of the downloaded artifact.
     *

     * Default is {@link OfficialArtifactRepository#RABBITMQ}
     *
     * @see OfficialArtifactRepository
     */
    public Builder downloadFrom(ArtifactRepository repository) {
      this.artifactRepository = repository;
      return this;
    }

    /**
     * @param downloadSource the URL from which to download the OS-specific artifact. For example: {@code
     *                       https://github.com/rabbitmq/rabbitmq-server/releases/download/rabbitmq_v3_6_5/rabbitmq-server-generic-unix-3.6.5.tar.xz}
     * @param appFolderName  this is the name of the folder under which all application files are found. For example,
     *                       {@code rabbitmq-server-generic-unix-3.6.5.tar.xz} extracts everything into to {@code
     *                       rabbitmq_server-3.6.5/*}. Therefore, the value here should be {@code
     *                       "rabbitmq_server-3.6.5"}
     *
     * @see #version(Version)
     */
    public Builder downloadFrom(final URL downloadSource, final String appFolderName) {
      this.artifactRepository = new CustomArtifactRepository(downloadSource);

      this.version = new CustomVersion(appFolderName);
      return this;
    }

    /**
     * Use a predefined version of RabbitMQ.
     *

     * {@link PredefinedVersion} establishes the download URL using the official RabbitMQ artifact repository,
     * the appropriate artifact type depending on the current Operating System, and information about the extraction
     * folder.
     *

     * Default value if none is specified: {@link PredefinedVersion#LATEST}
     *
     * @see #downloadFrom(URL, String)
     */
    public Builder version(Version version) {
      this.version = version;
      return this;
    }

    /**
     * Use this method to define a location where the compressed artifact will be downloaded to.
     *

     * If the folder doesn't already exist, it will be created.
     *

     * By default, the download folder will be: ~/{@value DOWNLOAD_FOLDER}, while the file name will match the
     * remote artifact's name (as defined by the download URL).
     *

     * Use the {@link #downloadTarget(File)} method to specify a file as destination instead of a folder, but not both.
     */
    public Builder downloadFolder(File downloadFolder) {
      if (this.downloadTarget != null) {
        throw new IllegalStateException("Download Target has already been set.");
      }
      this.downloadFolder = downloadFolder;
      return this;
    }

    /**
     * Use this method to specify a file which should be used to store the downloaded artifact.
     *

     * If the file already exists, it will be overwritten.
     *

     * Use {@link #downloadFolder(File)} to define a folder instead of a file if necessary, but not both.
     *

     * By default, the download folder will be: ~/{@value DOWNLOAD_FOLDER}, while the file name will match the
     * remote artifact's name (as defined by the download URL).
     */
    public Builder downloadTarget(File downloadTarget) {
      if (this.downloadFolder != null) {
        throw new IllegalStateException("Download Folder has already been set.");
      }
      this.downloadTarget = downloadTarget;
      return this;
    }

    /**
     * Define a folder where the artifact should be extracted to.
     *

     * By default, the artifact will be extracted to Java's Temp folder.
     *
     * @see SystemUtils#JAVA_IO_TMPDIR
     */
    public Builder extractionFolder(File extractionFolder) {
      this.extractionFolder = extractionFolder;
      return this;
    }

    /**
     * If the artifact is already present in the filesystem, setting this as {code true} will prevent re-downloading it.
     *

     * Default value is {@code true}
     */
    public Builder useCachedDownload(boolean cacheDownload) {
      this.cacheDownload = cacheDownload;
      return this;
    }

    /**
     * If there's an issue downloading or extracting the artifact, automatically delete from downloaded file to prevent
     * future re-use.
     *

     * Default value is {@code true}
     */
    public Builder deleteDownloadedFileOnErrors(boolean deleteCachedFile) {
      this.deleteCachedFile = deleteCachedFile;
      return this;
    }

    /**
     * Defines an environment variable value to use for the execution of all RabbitMQ commands.
     *
     * @see RabbitMQ Environment
     * Variables
     * @see #envVar(RabbitMqEnvVar, String)
     */
    public Builder envVar(String key, String value) {
      this.envVars.put(key, value);
      return this;
    }

    /**
     * Defines an environment variable value to use for the execution of all RabbitMQ commands.
     *

     * Use {@link #envVar(String, String)} to define a variable that's not predefined in the {@link RabbitMqEnvVar} enum
     */
    public Builder envVar(RabbitMqEnvVar key, String value) {
      this.envVar(key.getEnvVarName(), value);
      return this;
    }

    /**
     * Defines the port that this RabbitMQ broker node will run on.
     *

     * The port is defined by setting the environment variable {@link RabbitMqEnvVar#NODE_PORT}
     *
     * @param port any valid port number or {@code -1} to use any random available port (same as {@link #randomPort()})
     */
    public Builder port(int port) {
      if (port == -1) {
        return this.randomPort();
      } else {
        this.envVar(RabbitMqEnvVar.NODE_PORT, String.valueOf(port));
        return this;
      }
    }

    /**
     * Defines the port that this RabbitMQ broker node will run on.
     *

     * The port is defined by setting the environment variable {@link RabbitMqEnvVar#NODE_PORT}
     */
    public Builder randomPort() {
      int randomPort = new RandomPortSupplier().get();
      return port(randomPort);
    }

    /**
     * A helper method to define several variables at once.
     *
     * @see #envVar(String, String)
     * @see #envVar(RabbitMqEnvVar, String)
     */
    public Builder envVars(Map<String, String> map) {
      this.envVars.putAll(map);
      return this;
    }

    public Builder processExecutorFactory(RabbitMqCommand.ProcessExecutorFactory factory) {
      this.processExecutorFactory = factory;
      return this;
    }

    public Builder downloadProxy(String hostname, int port) {
      return downloadProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(hostname, port)));
    }

    public Builder downloadProxy(Proxy downloadProxy) {
      this.downloadProxy = downloadProxy;
      return this;
    }

    /**
     * Builds an immutable instance of {@link EmbeddedRabbitMqConfig} using smart defaults.
     */
    public EmbeddedRabbitMqConfig build() {
      if (artifactRepository == null) {
        artifactRepository = OfficialArtifactRepository.GITHUB;
      }

      OperatingSystem os = OperatingSystem.detect();

      if (version == null) {
        version = PredefinedVersion.LATEST;
      }

      URL downloadSource = artifactRepository.getUrl(version, os);

      if (downloadTarget == null) {
        String filename = downloadSource.getPath().substring(downloadSource.getPath().lastIndexOf("/"));
        this.downloadTarget = new File(downloadFolder, filename);
      }

      if (extractionFolder == null) {
        this.extractionFolder = new File(SystemUtils.JAVA_IO_TMPDIR);
      }

      File appAbsPath = new File(extractionFolder.toString(), version.getExtractionFolder());

      return new EmbeddedRabbitMqConfig(
          version,
          downloadSource, downloadTarget, extractionFolder, appAbsPath,
          downloadConnectionTimeoutInMillis, downloadReadTimeoutInMillis,
          defaultRabbitMqCtlTimeoutInMillis,
          rabbitMqServerInitializationTimeoutInMillis,
          erlangCheckTimeoutInMillis,
          cacheDownload, deleteCachedFile,
          envVars,
          processExecutorFactory,
          downloadProxy);
    }

  }

}


package io.arivera.oss.embedded.rabbitmq;

import io.arivera.oss.embedded.rabbitmq.download.DownloadException;
import io.arivera.oss.embedded.rabbitmq.download.Downloader;
import io.arivera.oss.embedded.rabbitmq.download.DownloaderFactory;
import io.arivera.oss.embedded.rabbitmq.extract.ExtractionException;
import io.arivera.oss.embedded.rabbitmq.extract.Extractor;
import io.arivera.oss.embedded.rabbitmq.extract.ExtractorFactory;
import io.arivera.oss.embedded.rabbitmq.helpers.ErlangVersionChecker;
import io.arivera.oss.embedded.rabbitmq.helpers.ErlangVersionException;
import io.arivera.oss.embedded.rabbitmq.helpers.ShutDownException;
import io.arivera.oss.embedded.rabbitmq.helpers.ShutdownHelper;
import io.arivera.oss.embedded.rabbitmq.helpers.StartupException;
import io.arivera.oss.embedded.rabbitmq.helpers.StartupHelper;

import org.zeroturnaround.exec.ProcessResult;

import java.util.concurrent.Future;

/**
 * This is the main class to interact with RabbitMQ.
 *

 * Example use:
 *

 * {@code
 *   EmbeddedRabbitMqConfig config = new EmbeddedRabbitMqConfig.Builder().build();
 *   EmbeddedRabbitMq rabbitMq = new EmbeddedRabbitMq(config);
 *   rabbitMq.start();
 *   // ...
 *   rabbitMq.stop();
 * }
 *

 *
 * @see EmbeddedRabbitMqConfig
 */
public class EmbeddedRabbitMq {

  private EmbeddedRabbitMqConfig config;
  private Future<ProcessResult> rabbitMqProcess;

  public EmbeddedRabbitMq(EmbeddedRabbitMqConfig config) {
    this.config = config;
  }

  /**
   * Starts the RabbitMQ server process and blocks the current thread until the initialization is completed.
   *
   * @throws ErlangVersionException when there's an issue with the system's Erlang version
   * @throws DownloadException    when there's an issue downloading the appropriate artifact
   * @throws ExtractionException  when there's an issue extracting the files from the downloaded artifact
   * @throws StartupException     when there's an issue starting the RabbitMQ server
   */
  public void start() throws ErlangVersionException, DownloadException, ExtractionException, StartupException {
    if (rabbitMqProcess != null) {
      throw new IllegalStateException("Start shouldn't be called more than once unless stop() has been called before.");
    }

    check();
    download();
    extract();
    run();
  }

  private void check() throws ErlangVersionException {
    new ErlangVersionChecker(config).check();
  }

  private void download() throws DownloadException {
    Downloader downloader = new DownloaderFactory(config).getNewInstance();
    downloader.run();
  }

  private void extract() throws ExtractionException {
    Extractor extractor = new ExtractorFactory(config).getNewInstance();
    extractor.run();
  }

  private void run() throws StartupException {
    rabbitMqProcess = new StartupHelper(config).call();
  }

  /**
   * Submits the command to stop RabbitMQ and blocks the current thread until the shutdown is completed.
   *
   * @throws ShutDownException if there's an issue shutting down the RabbitMQ server
   */
  public void stop() throws ShutDownException {
    if (rabbitMqProcess == null) {
      throw new IllegalStateException("Stop shouldn't be called unless 'start()' was successful.");
    }
    new ShutdownHelper(config, rabbitMqProcess).run();
    rabbitMqProcess = null;
  }

}


/*
 * 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 io.arivera.oss.embedded.rabbitmq.apache.commons.lang3;

import java.util.concurrent.TimeUnit;

/**
 *

 * StopWatch provides a convenient API for timings.
 *

 *
 *

 * To start the watch, call {@link #start()} or {@link StopWatch#createStarted()}. At this point you can:
 *

 *

 *
{@link #split()} the watch to get the time whilst the watch continues in the background. {@link #unsplit()} will
 * remove the effect of the split. At this point, these three options are available again.

 *
{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch to continue. Any time between the
 * suspend and resume will not be counted in the total. At this point, these three options are available again.

 *
{@link #stop()} the watch to complete the timing session.

 *

 *
 *

 * It is intended that the output methods {@link #toString()} and {@link #getTime()} should only be called after stop,
 * split or suspend, however a suitable result will be returned at other points.
 *

 *
 *

 * NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you cannot now call stop before start,
 * resume before suspend or unsplit before split.
 *

 *
 *

 * 1. split(), suspend(), or stop() cannot be invoked twice

 * 2. unsplit() may only be called if the watch has been split()

 * 3. resume() may only be called if the watch has been suspend()

 * 4. start() cannot be called twice without calling reset()
 *

 *
 *
This class is not thread-safe

 *
 * @since 2.0
 */
@SuppressWarnings("all")
public class StopWatch {

  private static final long NANO_2_MILLIS = 1000000L;

  /**
   * Provides a started stopwatch for convenience.
   *
   * @return StopWatch a stopwatch that's already been started.
   *
   * @since 3.5
   */
  public static StopWatch createStarted() {
    StopWatch sw = new StopWatch();
    sw.start();
    return sw;
  }

  /**
   * Enumeration type which indicates the status of stopwatch.
   */
  private enum State {

    UNSTARTED {
      @Override boolean isStarted() { return false; }
      @Override boolean isStopped() { return true;  }
      @Override boolean isSuspended() { return false; }
    },
    RUNNING {
      @Override boolean isStarted() { return true; }
      @Override boolean isStopped() { return false; }
      @Override boolean isSuspended() { return false; }
    },
    STOPPED {
      @Override boolean isStarted() { return false; }
      @Override boolean isStopped() { return true; }
      @Override boolean isSuspended() { return false; }
    },
    SUSPENDED {
      @Override boolean isStarted() { return true; }
      @Override boolean isStopped() { return false; }
      @Override  boolean isSuspended() { return true; }
    };

    /**
     *

     * The method is used to find out if the StopWatch is started. A suspended
     * StopWatch is also started watch.
     *

     * @return boolean
     *             If the StopWatch is started.
     */
    abstract boolean isStarted();

    /**
     *

     * This method is used to find out whether the StopWatch is stopped. The
     * stopwatch which's not yet started and explicitly stopped stopwatch is
     * considered as stopped.
     *

     *
     * @return boolean
     *             If the StopWatch is stopped.
     */
    abstract boolean isStopped();

    /**
     *

     * This method is used to find out whether the StopWatch is suspended.
     *

     *
     * @return boolean
     *             If the StopWatch is suspended.
     */
    abstract boolean isSuspended();
  }

  /**
   * Enumeration type which indicates the split status of stopwatch.
   */
  private enum SplitState {
    SPLIT,
    UNSPLIT
  }
  /**
   * The current running state of the StopWatch.
   */
  private State runningState = State.UNSTARTED;

  /**
   * Whether the stopwatch has a split time recorded.
   */
  private SplitState splitState = SplitState.UNSPLIT;

  /**
   * The start time.
   */
  private long startTime;

  /**
   * The start time in Millis - nanoTime is only for elapsed time so we
   * need to also store the currentTimeMillis to maintain the old
   * getStartTime API.
   */
  private long startTimeMillis;

  /**
   * The stop time.
   */
  private long stopTime;

  /**
   *

   * Constructor.
   *

   */
  public StopWatch() {
    super();
  }

  /**
   *

   * Start the stopwatch.
   *

   *
   *

   * This method starts a new timing session, clearing any previous values.
   *

   *
   * @throws IllegalStateException
   *             if the StopWatch is already running.
   */
  public void start() {
    if (this.runningState == State.STOPPED) {
      throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
    }
    if (this.runningState != State.UNSTARTED) {
      throw new IllegalStateException("Stopwatch already started. ");
    }
    this.startTime = System.nanoTime();
    this.startTimeMillis = System.currentTimeMillis();
    this.runningState = State.RUNNING;
  }

  /**
   *

   * Stop the stopwatch.
   *

   *
   *

   * This method ends a new timing session, allowing the time to be retrieved.
   *

   *
   * @throws IllegalStateException
   *             if the StopWatch is not running.
   */
  public void stop() {
    if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) {
      throw new IllegalStateException("Stopwatch is not running. ");
    }
    if (this.runningState == State.RUNNING) {
      this.stopTime = System.nanoTime();
    }
    this.runningState = State.STOPPED;
  }

  /**
   *

   * Resets the stopwatch. Stops it if need be.
   *

   *
   *

   * This method clears the internal values to allow the object to be reused.
   *

   */
  public void reset() {
    this.runningState = State.UNSTARTED;
    this.splitState = SplitState.UNSPLIT;
  }

  /**
   *

   * Split the time.
   *

   *
   *

   * This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected,
   * enabling {@link #unsplit()} to continue the timing from the original start point.
   *

   *
   * @throws IllegalStateException
   *             if the StopWatch is not running.
   */
  public void split() {
    if (this.runningState != State.RUNNING) {
      throw new IllegalStateException("Stopwatch is not running. ");
    }
    this.stopTime = System.nanoTime();
    this.splitState = SplitState.SPLIT;
  }

  /**
   *

   * Remove a split.
   *

   *
   *

   * This method clears the stop time. The start time is unaffected, enabling timing from the original start point to
   * continue.
   *

   *
   * @throws IllegalStateException
   *             if the StopWatch has not been split.
   */
  public void unsplit() {
    if (this.splitState != SplitState.SPLIT) {
      throw new IllegalStateException("Stopwatch has not been split. ");
    }
    this.splitState = SplitState.UNSPLIT;
  }

  /**
   *

   * Suspend the stopwatch for later resumption.
   *

   *
   *

   * This method suspends the watch until it is resumed. The watch will not include time between the suspend and
   * resume calls in the total time.
   *

   *
   * @throws IllegalStateException
   *             if the StopWatch is not currently running.
   */
  public void suspend() {
    if (this.runningState != State.RUNNING) {
      throw new IllegalStateException("Stopwatch must be running to suspend. ");
    }
    this.stopTime = System.nanoTime();
    this.runningState = State.SUSPENDED;
  }

  /**
   *

   * Resume the stopwatch after a suspend.
   *

   *
   *

   * This method resumes the watch after it was suspended. The watch will not include time between the suspend and
   * resume calls in the total time.
   *

   *
   * @throws IllegalStateException
   *             if the StopWatch has not been suspended.
   */
  public void resume() {
    if (this.runningState != State.SUSPENDED) {
      throw new IllegalStateException("Stopwatch must be suspended to resume. ");
    }
    this.startTime += System.nanoTime() - this.stopTime;
    this.runningState = State.RUNNING;
  }

  /**
   *

   * Get the time on the stopwatch.
   *

   *
   *

   * This is either the time between the start and the moment this method is called, or the amount of time between
   * start and stop.
   *

   *
   * @return the time in milliseconds
   */
  public long getTime() {
    return getNanoTime() / NANO_2_MILLIS;
  }

  /**
   *

   * Get the time on the stopwatch in the specified TimeUnit.
   *

   *
   *

   * This is either the time between the start and the moment this method is called, or the amount of time between
   * start and stop. The resulting time will be expressed in the desired TimeUnit with any remainder rounded down.
   * For example, if the specified unit is {@code TimeUnit.HOURS} and the stopwatch time is 59 minutes, then the
   * result returned will be {@code 0}.
   *

   *
   * @param timeUnit the unit of time, not null
   * @return the time in the specified TimeUnit, rounded down
   * @since 3.5
   */
  public long getTime(final TimeUnit timeUnit) {
    return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS);
  }

  /**
   *

   * Get the time on the stopwatch in nanoseconds.
   *

   *
   *

   * This is either the time between the start and the moment this method is called, or the amount of time between
   * start and stop.
   *

   *
   * @return the time in nanoseconds
   * @since 3.0
   */
  public long getNanoTime() {
    if (this.runningState == State.STOPPED || this.runningState == State.SUSPENDED) {
      return this.stopTime - this.startTime;
    } else if (this.runningState == State.UNSTARTED) {
      return 0;
    } else if (this.runningState == State.RUNNING) {
      return System.nanoTime() - this.startTime;
    }
    throw new RuntimeException("Illegal running state has occurred.");
  }

  /**
   *

   * Get the split time on the stopwatch.
   *

   *
   *

   * This is the time between start and latest split.
   *

   *
   * @return the split time in milliseconds
   *
   * @throws IllegalStateException
   *             if the StopWatch has not yet been split.
   * @since 2.1
   */
  public long getSplitTime() {
    return getSplitNanoTime() / NANO_2_MILLIS;
  }
  /**
   *

   * Get the split time on the stopwatch in nanoseconds.
   *

   *
   *

   * This is the time between start and latest split.
   *

   *
   * @return the split time in nanoseconds
   *
   * @throws IllegalStateException
   *             if the StopWatch has not yet been split.
   * @since 3.0
   */
  public long getSplitNanoTime() {
    if (this.splitState != SplitState.SPLIT) {
      throw new IllegalStateException("Stopwatch must be split to get the split time. ");
    }
    return this.stopTime - this.startTime;
  }

  /**
   * Returns the time this stopwatch was started.
   *
   * @return the time this stopwatch was started
   * @throws IllegalStateException
   *             if this StopWatch has not been started
   * @since 2.4
   */
  public long getStartTime() {
    if (this.runningState == State.UNSTARTED) {
      throw new IllegalStateException("Stopwatch has not been started");
    }
    // System.nanoTime is for elapsed time
    return this.startTimeMillis;
  }

//  /**
//   *

//   * Gets a summary of the time that the stopwatch recorded as a string.
//   *

//   *
//   *

//   * The format used is ISO 8601-like, hours:minutes:seconds.milliseconds.
//   *

//   *
//   * @return the time as a String
//   */
//  @Override
//  public String toString() {
//    return DurationFormatUtils.formatDurationHMS(getTime());
//  }

//  /**
//   *

//   * Gets a summary of the split time that the stopwatch recorded as a string.
//   *

//   *
//   *

//   * The format used is ISO 8601-like, hours:minutes:seconds.milliseconds.
//   *

//   *
//   * @return the split time as a String
//   * @since 2.1
//   */
//  public String toSplitString() {
//    return DurationFormatUtils.formatDurationHMS(getSplitTime());
//  }

  /**
   *

   * The method is used to find out if the StopWatch is started. A suspended
   * StopWatch is also started watch.
   *

   *
   * @return boolean
   *             If the StopWatch is started.
   * @since 3.2
   */
  public boolean isStarted() {
    return runningState.isStarted();
  }

  /**
   *

   * This method is used to find out whether the StopWatch is suspended.
   *

   *
   * @return boolean
   *             If the StopWatch is suspended.
   * @since 3.2
   */
  public boolean isSuspended() {
    return runningState.isSuspended();
  }

  /**
   *

   * This method is used to find out whether the StopWatch is stopped. The
   * stopwatch which's not yet started and explicitly stopped stopwatch is
   * considered as stopped.
   *

   *
   * @return boolean
   *             If the StopWatch is stopped.
   * @since 3.2
   */
  public boolean isStopped() {
    return runningState.isStopped();
  }

}


package io.arivera.oss.embedded.rabbitmq.util;

import java.util.Collection;

public class StringUtils {

  /**
   * Joins the elements of the provided collection into a single String containing the provided list of elements.
   *

   * No delimiter is added before or after the list.
   *

   * Empty collections return an empty String.
   */
  public static <T> String join(Collection<T> collection, CharSequence joinedBy) {
    if (collection.isEmpty()) {
      return "";
    }
    StringBuilder stringBuilder = new StringBuilder(256);
    for (T t : collection) {
      stringBuilder.append(t.toString()).append(joinedBy);
    }
    return stringBuilder.substring(0, stringBuilder.length() - joinedBy.length());
  }

}


package io.arivera.oss.embedded.rabbitmq.helpers;

import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMqConfig;
import io.arivera.oss.embedded.rabbitmq.bin.ErlangShell;
import io.arivera.oss.embedded.rabbitmq.bin.ErlangShellException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A class that helps enforce the existence and version requirements of Erlang to run RabbitMQ.
 */
public class ErlangVersionChecker {

  private static final Logger LOGGER = LoggerFactory.getLogger(ErlangVersionChecker.class);

  private final ErlangShell erlangShell;
  private final String minErlangVersion;

  public ErlangVersionChecker(EmbeddedRabbitMqConfig config) {
    this(config.getVersion().getMinimumErlangVersion(), new ErlangShell(config));
  }

  public ErlangVersionChecker(String minErlangVersion, ErlangShell erlangShell) {
    this.minErlangVersion = minErlangVersion;
    this.erlangShell = erlangShell;
  }

  /**
   * Retrieves the current system's Erlang version to compare it to the minimum required version.
   * <p>
   * The system's Erlang version is always retrieved, but the comparison might be skipped if the RabbitMQ version
   * doesn't specify a minimum required version.
   *
   * @throws ErlangVersionException if the minimum required version is not met or if it can't be determined.
   */
  public void check() throws ErlangVersionException {
    String erlangVersion;
    try {
      erlangVersion = erlangShell.getErlangVersion();
      LOGGER.debug("Erlang version installed in this system: {}", erlangVersion);
    } catch (ErlangShellException e) {
      throw new ErlangVersionException("Could not determine Erlang version. Ensure Erlang is correctly installed.", e);
    }

    if (minErlangVersion == null) {
      LOGGER.debug("RabbitMQ version to execute doesn't specify a minimum Erlang version. Will skip this check.");
      return;
    } else {
      LOGGER.debug("RabbitMQ version to execute requires Erlang version {} or above.", minErlangVersion);
    }

    int[] expected;
    int[] actual;
    try {
      expected = parse(minErlangVersion);
      actual = parse(erlangVersion);
    } catch (RuntimeException e) {
      LOGGER.warn("Error parsing Erlang version: " + minErlangVersion + " or " + erlangVersion + ". Ignoring check...");
      return;
    }

    for (int i = 0; i < actual.length; i++) {
      if (actual[i] > expected[i]) {
        break;
      }
      if (actual[i] < expected[i]) {
        throw new ErlangVersionException(
            String.format("Minimum required Erlang version not found. Expected '%s' or higher. Actual is: '%s'",
                minErlangVersion, erlangVersion));
      }
    }
  }

  /**
   * @return a numeric value useful for comparing versions.
   */
  static int[] parse(String erlangVersion) {
    int[] version = {0, 0, 0, 0, 0};
    if (erlangVersion.startsWith("R")) {
      erlangVersion = erlangVersion.substring(1);                     // "R15B03-1" -> "15B03-1"
      String[] components = erlangVersion.split("\\D", 2);            // "15B03-1" -> ["15", "03-1"]
      version[0] = Integer.parseInt(components[0]);                        // "15" -> 15
      version[1] = erlangVersion.replaceAll("[^A-Z]", "").charAt(0);       // "15B03-1-" -> "B" -> 66
      if (components.length >= 2
          && !components[1].isEmpty()
          && components[1].indexOf("-") != 0) {
        version[2] = Integer.parseInt(components[1].split("-", 2)[0]);     // "03-1" -> "03" -> 3
      }
    } else {
      String[] components = erlangVersion.split("\\.", 5);
      for (int i = 0; i < components.length; i++) {
        version[i] = Integer.parseInt(components[i]);
      }
    }
    return version;
  }
}


package io.arivera.oss.embedded.rabbitmq.bin;

import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMqConfig;
import io.arivera.oss.embedded.rabbitmq.bin.plugins.Plugin;

import org.zeroturnaround.exec.ProcessResult;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * A wrapper around the RabbitMqCommand to execute '{@value #EXECUTABLE}' commands.
 *
 * @see rabbitmq-plugins(1) manual page
 */
public class RabbitMqPlugins {

  private static final String LIST_COMMAND = "list";
  private static final String EXECUTABLE = "rabbitmq-plugins";

  private final EmbeddedRabbitMqConfig config;

  public RabbitMqPlugins(EmbeddedRabbitMqConfig config) {
    this.config = config;
  }

  /**
   * This method exposes a way to invoke '{@value EXECUTABLE}' command with any arguments.
   * This is useful when the class methods don't expose the desired functionality.
   *

   * For example:
   *

   * RabbitMqPlugins command = new RabbitMqPlugins(config);
   * command.execute("list", "-v", "management");
   *

   *
   * @throws RabbitMqCommandException if the command cannot be executed
   */
  public Future<ProcessResult> execute(String... arguments) throws RabbitMqCommandException {
    return new RabbitMqCommand(config, EXECUTABLE, arguments)
        .call()
        .getFuture();
  }

  /**
   * Same as {@link #list()} but it returns the plugins grouped by state.
   */
  public Map<Plugin.State, Set<Plugin>> groupedList() throws RabbitMqCommandException {
    Collection<Plugin> plugins = list().values();
    return groupPluginsByState(plugins);
  }

  private Map<Plugin.State, Set<Plugin>> groupPluginsByState(Collection<Plugin> plugins) {
    Map<Plugin.State, Set<Plugin>> groupedPlugins = new HashMap<>();
    for (Plugin.State state : Plugin.State.values()) {
      groupedPlugins.put(state, new TreeSet<Plugin>());
    }

    for (Plugin plugin : plugins) {
      for (Plugin.State state : plugin.getState()) {
        groupedPlugins.get(state).add(plugin);
      }
    }
    return groupedPlugins;
  }

  /**
   * Executes the {@code rabbitmq-plugins list} command
   *
   * @return a Map where the key is the plugin name and the value is the full plugin details parsed from the output.
   *
   * @throws RabbitMqCommandException if the command cannot be executed, it doesn't
   *                                  {@link EmbeddedRabbitMqConfig.Builder#defaultRabbitMqCtlTimeoutInMillis(long)
   *                                  finish in time} or exits unexpectedly
   * @see #groupedList()
   */
  public Map<String, Plugin> list() {
    String[] args = {LIST_COMMAND};
    String executionErrorMessage = String.format("Error executing: %s %s", EXECUTABLE, LIST_COMMAND);
    String unexpectedExitCodeMessage = "Listing of plugins failed with exit code: ";

    ProcessResult processResult = getProcessResult(args, executionErrorMessage, unexpectedExitCodeMessage);

    List<Plugin> plugins = parseListOutput(processResult);
    Map<String, Plugin> result = mapPluginsByName(plugins);
    return result;
  }

  private List<Plugin> parseListOutput(ProcessResult processResult) {
    List<String> lines = processResult.getOutput().getLinesAsUTF8();
    return Plugin.fromStrings(lines);
  }

  private Map<String, Plugin> mapPluginsByName(List<Plugin> plugins) {
    Map<String, Plugin> result = new HashMap<>(plugins.size());
    for (Plugin plugin : plugins) {
      result.put(plugin.getName(), plugin);
    }
    return result;
  }

  /**
   * Executes the command {@code rabbitmq-plugins enable {plugin}} and blocks until the call finishes.
   *
   * @param plugin the name of the plugin to enable.
   *
   * @throws RabbitMqCommandException if the command cannot be executed, it doesn't
   *                                  {@link EmbeddedRabbitMqConfig.Builder#defaultRabbitMqCtlTimeoutInMillis(long)
   *                                  finish in time} or exits unexpectedly
   */
  public void enable(String plugin) throws RabbitMqCommandException {
    String[] args = {"enable", plugin};
    String executionErrorMessage = "Error while enabling plugin '" + plugin + "'";
    String unexpectedExitCodeMessage = "Enabling of plugin '" + plugin + "' failed with exit code: ";

    getProcessResult(args, executionErrorMessage, unexpectedExitCodeMessage);
  }

  /**
   * Disables the given plugin by executing {@code rabbitmq-plugins disable {plugin}} and blocks until the call is
   * finished.
   *
   * @param plugin the name of the plugin to disable.
   *
   * @throws RabbitMqCommandException if the command cannot be executed, it doesn't
   *                                  {@link EmbeddedRabbitMqConfig.Builder#defaultRabbitMqCtlTimeoutInMillis(long)
   *                                  finish in time} or exits unexpectedly
   */
  public void disable(String plugin) throws RabbitMqCommandException {
    String[] args = {"disable", plugin};
    String executionErrorMessage = "Error while disabling plugin '" + plugin + "'";
    String unexpectedExitCodeMessage = "Disabling of plugin '" + plugin + "' failed with exit code: ";

    getProcessResult(args, executionErrorMessage, unexpectedExitCodeMessage);
  }

  private ProcessResult getProcessResult(String[] args, String executionErrorMessage, String unexpectedExitCodeMessage) {
    ProcessResult processResult;
    try {
      Future<ProcessResult> startedProcess = execute(args);
      processResult = startedProcess.get(config.getDefaultRabbitMqCtlTimeoutInMillis(), TimeUnit.MILLISECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
      throw new RabbitMqCommandException(executionErrorMessage, e);
    }

    int exitValue = processResult.getExitValue();
    if (exitValue != 0) {
      throw new RabbitMqCommandException(unexpectedExitCodeMessage + exitValue);
    }
    return processResult;
  }

}