net.ftb.util.OSUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.ftb.util.OSUtils.java

Source

/*
 * This file is part of FTB Launcher.
 *
 * Copyright  2012-2014, FTB Launcher Contributors <https://github.com/Slowpoke101/FTBLaunch/>
 * FTB Launcher is licensed 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 net.ftb.util;

import com.sun.jna.Library;
import com.sun.jna.Native;
import lombok.Getter;
import net.ftb.data.CommandLineSettings;
import net.ftb.gui.LaunchFrame;
import net.ftb.log.Logger;
import net.ftb.util.winreg.JavaFinder;
import net.ftb.util.winreg.RuntimeStreamer;
import org.apache.commons.io.FileUtils;

import java.awt.*;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.CodeSource;
import java.util.Enumeration;
import java.util.Map;
import java.util.UUID;

import javax.swing.text.html.StyleSheet;

public class OSUtils {
    private static byte[] cachedMacAddress;
    private static String cachedUserHome;

    /**
     * gets the number of cores for use in DL threading
     *
     * @return number of cores on the system
     */
    @Getter
    private static int numCores;
    private static byte[] hardwareID;

    private static UUID clientUUID;

    public static enum OS {
        WINDOWS, UNIX, MACOSX, OTHER,
    }

    static {
        cachedUserHome = System.getProperty("user.home");
        numCores = Runtime.getRuntime().availableProcessors();
    }

    /**
     * Gets the default installation path for the current OS.
     * @return a string containing the default install path for the current OS.
     */
    public static String getDefInstallPath() {
        switch (getCurrentOS()) {
        case WINDOWS:
            String defaultLocation = "c:\\ftb";
            File testFile = new File(defaultLocation);
            // existing directory and we can write
            if (testFile.canWrite()) {
                return defaultLocation;
            }

            // We can create default directory
            if (testFile.getParentFile().canWrite()) {
                return defaultLocation;
            }
            Logger.logWarn(
                    "Can't use default installation location. Using current location of the launcher executable.");

        case MACOSX:
            return System.getProperty("user.home") + "/ftb";
        case UNIX:
            return System.getProperty("user.home") + "/ftb";
        default:
            try {
                CodeSource codeSource = LaunchFrame.class.getProtectionDomain().getCodeSource();
                File jarFile;
                jarFile = new File(codeSource.getLocation().toURI().getPath());
                return jarFile.getParentFile().getPath();
            } catch (URISyntaxException e) {
                Logger.logError("Unexcepted error", e);
            }

            return System.getProperty("user.home") + System.getProperty("path.separator") + "FTB";
        }
    }

    /**
     * Used to get the dynamic storage location based off OS
     * @return string containing dynamic storage location
     */
    public static String getDynamicStorageLocation() {
        if (CommandLineSettings.getSettings().getDynamicDir() != null
                && !CommandLineSettings.getSettings().getDynamicDir().isEmpty()) {
            return CommandLineSettings.getSettings().getDynamicDir();
        }
        switch (getCurrentOS()) {
        case WINDOWS:
            return System.getenv("APPDATA") + "/ftblauncher/";
        case MACOSX:
            return cachedUserHome + "/Library/Application Support/ftblauncher/";
        case UNIX:
            return cachedUserHome + "/.ftblauncher/";
        default:
            return getDefInstallPath() + "/temp/";
        }
    }

    /**
     * Used to get a location to store cached content such as maps,
     * texture packs and pack archives.
     *
     * @return string containing cache storage location
     */
    public static String getCacheStorageLocation() {
        if (CommandLineSettings.getSettings().getCacheDir() != null
                && !CommandLineSettings.getSettings().getCacheDir().isEmpty()) {
            return CommandLineSettings.getSettings().getCacheDir();
        }
        switch (getCurrentOS()) {
        case WINDOWS:
            if (System.getenv("LOCALAPPDATA") != null && System.getenv("LOCALAPPDATA").length() > 5) {
                return System.getenv("LOCALAPPDATA") + "/ftblauncher/";
            } else {
                return System.getenv("APPDATA") + "/ftblauncher/";
            }
        case MACOSX:
            return cachedUserHome + "/Library/Application Support/ftblauncher/";
        case UNIX:
            return cachedUserHome + "/.ftblauncher/";
        default:
            return getDefInstallPath() + "/temp/";
        }
    }

    public static void createStorageLocations() {
        File cacheDir = new File(OSUtils.getCacheStorageLocation());
        File dynamicDir = new File(OSUtils.getDynamicStorageLocation());

        if (!cacheDir.exists()) {
            cacheDir.mkdirs();

            if (dynamicDir.exists() && !cacheDir.equals(dynamicDir)) {
                // Migrate cached archives from the user's roaming profile to their local cache

                Logger.logInfo("Migrating cached Maps from Roaming to Local storage");
                FTBFileUtils.move(new File(dynamicDir, "Maps"), new File(cacheDir, "Maps"));

                Logger.logInfo("Migrating cached Modpacks from Roaming to Local storage");
                FTBFileUtils.move(new File(dynamicDir, "ModPacks"), new File(cacheDir, "ModPacks"));

                Logger.logInfo("Migrating cached Texturepacks from Roaming to Local storage");
                FTBFileUtils.move(new File(dynamicDir, "TexturePacks"), new File(cacheDir, "TexturePacks"));

                Logger.logInfo("Migration complete.");
            }
        }

        if (!dynamicDir.exists()) {
            dynamicDir.mkdirs();
        }

        if (getCurrentOS() == OS.WINDOWS) {
            File oldLoginData = new File(dynamicDir, "logindata");
            File newLoginData = new File(cacheDir, "logindata");
            try {
                if (oldLoginData.exists()
                        && !oldLoginData.getCanonicalPath().equals(newLoginData.getCanonicalPath())) {
                    newLoginData.delete();
                }
            } catch (Exception e) {
                Logger.logError("Error deleting login data", e);
            }
        }
    }

    public static long getOSTotalMemory() {
        return getOSMemory("getTotalPhysicalMemorySize", "Could not get RAM Value");
    }

    public static long getOSFreeMemory() {
        return getOSMemory("getFreePhysicalMemorySize", "Could not get free RAM Value");
    }

    private static long getOSMemory(String methodName, String warning) {
        long ram = 0;

        OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
        Method m;
        try {
            m = operatingSystemMXBean.getClass().getDeclaredMethod(methodName);
            m.setAccessible(true);
            Object value = m.invoke(operatingSystemMXBean);
            if (value != null) {
                ram = Long.valueOf(value.toString()) / 1024 / 1024;
            } else {
                Logger.logWarn(warning);
                ram = 1024;
            }
        } catch (Exception e) {
            Logger.logError("Error while getting OS memory info", e);
        }

        return ram;
    }

    /**
     * Used to get the java delimiter for current OS
     * @return string containing java delimiter for current OS
     */
    public static String getJavaDelimiter() {
        switch (getCurrentOS()) {
        case WINDOWS:
            return ";";
        case UNIX:
            return ":";
        case MACOSX:
            return ":";
        default:
            return ";";
        }
    }

    /**
     * Used to get the current operating system
     * @return OS enum representing current operating system
     */
    public static OS getCurrentOS() {
        String osString = System.getProperty("os.name").toLowerCase();
        if (osString.contains("win")) {
            return OS.WINDOWS;
        } else if (osString.contains("nix") || osString.contains("nux")) {
            return OS.UNIX;
        } else if (osString.contains("mac")) {
            return OS.MACOSX;
        } else {
            return OS.OTHER;
        }
    }

    /**
     * Used to check if Windows is 64-bit
     * @return true if 64-bit Windows
     */
    public static boolean is64BitWindows() {
        String arch = System.getenv("PROCESSOR_ARCHITECTURE");
        String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
        return (arch.endsWith("64") || (wow64Arch != null && wow64Arch.endsWith("64")));
    }

    /**
     * Used to check if a posix OS is 64-bit
     * @return true if 64-bit Posix OS
     */
    public static boolean is64BitPosix() {
        String line, result = "";
        try {
            Process command = Runtime.getRuntime().exec("uname -m");
            BufferedReader in = new BufferedReader(new InputStreamReader(command.getInputStream()));
            while ((line = in.readLine()) != null) {
                result += (line + "\n");
            }
        } catch (Exception e) {
            Logger.logError("Posix bitness check failed", e);
        }
        // 32-bit Intel Linuces, it returns i[3-6]86. For 64-bit Intel, it says x86_64
        return result.contains("_64");
    }

    /**
     * Used to check if OS X is 64-bit
     * @return true if 64-bit OS X
     */

    public static boolean is64BitOSX() {
        String line, result = "";
        if (!(System.getProperty("os.version").startsWith("10.6")
                || System.getProperty("os.version").startsWith("10.5"))) {
            return true;//10.7+ only shipped on hardware capable of using 64 bit java
        }
        try {
            Process command = Runtime.getRuntime().exec("/usr/sbin/sysctl -n hw.cpu64bit_capable");
            BufferedReader in = new BufferedReader(new InputStreamReader(command.getInputStream()));
            while ((line = in.readLine()) != null) {
                result += (line + "\n");
            }
        } catch (Exception e) {
            Logger.logError("OS X bitness check failed", e);
        }
        return result.equals("1");
    }

    /**
     * Used to check if operating system is 64-bit
     * @return true if 64-bit operating system
     */
    public static boolean is64BitOS() {
        switch (getCurrentOS()) {
        case WINDOWS:
            return is64BitWindows();
        case UNIX:
            return is64BitPosix();
        case MACOSX:
            return is64BitOSX();
        case OTHER:
            return true;
        default:
            return true;
        }
    }

    /**
     * Used to get check if JVM is 64-bit
     * @return true if 64-bit JVM
     */
    public static Boolean is64BitVM() {
        Boolean bits64;
        if ((getCurrentOS() == OS.WINDOWS || getCurrentOS() == OS.MACOSX)
                && JavaFinder.parseJavaVersion() != null) {
            bits64 = JavaFinder.parseJavaVersion().is64bits;
        } else {
            bits64 = System.getProperty("sun.arch.data.model").equals("64");
        }
        return bits64;
    }

    /**
     * Used to get the OS name for use in google analytics
     * @return Linux/OSX/Windows/other/
     */
    public static String getOSString() {
        String osString = System.getProperty("os.name").toLowerCase();
        if (osString.contains("win")) {
            return "Windows";
        } else if (osString.contains("linux")) {
            return "linux";
        } else if (osString.contains("mac")) {
            return "OSX";
        } else {
            return osString;
        }
    }

    /**
     * sees if the hash of the UUID matches the one stored in the config
     * @return true if UUID matches hash or false if it does not
     */
    public static boolean verifyUUID() {
        return true;
    }

    /**
     * Grabs the mac address of computer and makes it 10 times longer
     * @return a byte array containing mac address
     */
    public static byte[] getMacAddress() {
        if (cachedMacAddress != null && cachedMacAddress.length >= 10) {
            return cachedMacAddress;
        }
        try {
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface network = networkInterfaces.nextElement();
                byte[] mac = network.getHardwareAddress();
                if (mac != null && mac.length > 0 && !network.isLoopback() && !network.isVirtual()
                        && !network.isPointToPoint() && network.getName().substring(0, 3) != "ham") {
                    Logger.logDebug("Interface: " + network.getDisplayName() + " : " + network.getName());
                    cachedMacAddress = new byte[mac.length * 10];
                    for (int i = 0; i < cachedMacAddress.length; i++) {
                        cachedMacAddress[i] = mac[i - (Math.round(i / mac.length) * mac.length)];
                    }
                    return cachedMacAddress;
                }
            }
        } catch (SocketException e) {
            Logger.logWarn("Exception getting MAC address", e);
        }

        Logger.logWarn("Failed to get MAC address, using default logindata key");
        return new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
    }

    /**
     *
     * @return Unique Id based on hardware
     */
    public static byte[] getHardwareID() {
        if (hardwareID == null) {
            hardwareID = genHardwareID();
        }
        return hardwareID;
    }

    private static byte[] genHardwareID() {
        switch (getCurrentOS()) {
        case WINDOWS:
            return genHardwareIDWINDOWS();
        case UNIX:
            return genHardwareIDUNIX();
        case MACOSX:
            return genHardwareIDMACOSX();
        default:
            return null;
        }
    }

    private static byte[] genHardwareIDUNIX() {
        String line;
        if (CommandLineSettings.getSettings().isUseMac()) {
            try {
                BufferedReader reader = new BufferedReader(new FileReader("/etc/machine-id"));
                line = reader.readLine();
            } catch (Exception e) {
                Logger.logDebug("failed", e);
                return new byte[] {};
            }
            return line.getBytes();
        } else {
            return new byte[] {};
        }
    }

    private static byte[] genHardwareIDMACOSX() {
        String line;
        try {
            Process command = Runtime.getRuntime().exec(new String[] { "system_profiler", "SPHardwareDataType" });
            BufferedReader in = new BufferedReader(new InputStreamReader(command.getInputStream()));
            while ((line = in.readLine()) != null) {
                if (line.contains("Serial Number"))
                //TODO: does that more checks?
                {
                    return line.split(":")[1].trim().getBytes();
                }
            }
            return new byte[] {};
        } catch (Exception e) {
            Logger.logDebug("failed", e);
            return new byte[] {};
        }
    }

    private static byte[] genHardwareIDWINDOWS() {
        String processOutput;
        try {
            processOutput = RuntimeStreamer.execute(new String[] { "wmic", "bios", "get", "serialnumber" });
            /*
             * wmic's output has special formatting:
             * SerialNumber<SP><SP><SP><CR><CR><LF>
             * 00000000000000000<SP><CR><CR><LF><CR><CR><LF>
             *
             * readLin()e uses <LF>, <CR> or <CR><LF> as line ending => we need to get third line from RuntimeStreamers output
             */
            String line = processOutput.split("\n")[2].trim();
            // at least VM will report serial to be 0. Does real hardware do it?
            if (line.equals("0")) {
                return new byte[] {};
            } else {
                return line.trim().getBytes();
            }
        } catch (Exception e) {
            Logger.logDebug("failed", e);
            return new byte[] {};
        }
    }

    /**
     * Opens the given URL in the default browser
     * @param url The URL
     */
    public static void browse(String url) {
        try {
            if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
                Desktop.getDesktop().browse(new URI(url.replace(" ", "+")));
            } else if (getCurrentOS() == OS.UNIX
                    && (new File("/usr/bin/xdg-open").exists() || new File("/usr/local/bin/xdg-open").exists())) {
                // Work-around to support non-GNOME Linux desktop environments with xdg-open installed
                new ProcessBuilder("xdg-open", url).start();
            } else {
                Logger.logWarn("Could not open Java Download url, not supported");
            }
        } catch (Exception e) {
            Logger.logError("Could not open link: " + url, e);
        }
    }

    /**
     * Opens the given path with the default application
     * @param path The path
     */
    public static void open(File path) {
        if (!path.exists()) {
            return;
        }
        try {
            if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
                Desktop.getDesktop().open(path);
            } else if (getCurrentOS() == OS.UNIX) {
                // Work-around to support non-GNOME Linux desktop environments with xdg-open installed
                if (new File("/usr/bin/xdg-open").exists() || new File("/usr/local/bin/xdg-open").exists()) {
                    new ProcessBuilder("xdg-open", path.toString()).start();
                }
            }
        } catch (Exception e) {
            Logger.logError("Could not open file", e);
        }
    }

    /**
     * @return if java 7+ can be ran on that version of osx
     */
    public static boolean canRun7OnMac() {
        return getCurrentOS() == OS.MACOSX && !(System.getProperty("os.version").startsWith("10.6")
                || System.getProperty("os.version").startsWith("10.5"));
    }

    /**
     * Removes environment variables which may cause faulty JVM memory allocations
     */
    public static void cleanEnvVars(Map<String, String> environment) {
        environment.remove("_JAVA_OPTIONS");
        environment.remove("JAVA_TOOL_OPTIONS");
        environment.remove("JAVA_OPTIONS");
    }

    public static StyleSheet makeStyleSheet(String name) {
        try {
            StyleSheet sheet = new StyleSheet();
            Reader reader = new InputStreamReader(System.class.getResourceAsStream("/css/" + name + ".css"));
            sheet.loadRules(reader, null);
            reader.close();

            return sheet;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    public static UUID getClientToken() {
        if (clientUUID != null) {
            return clientUUID;
        } else {
            String s = null;

            File tokenFile = new File(getCacheStorageLocation() + File.separator + "clientToken");
            if (tokenFile.exists() && tokenFile.isFile()) {
                try {
                    s = FileUtils.readFileToString(tokenFile);
                } catch (IOException e) {
                    s = null;
                    Logger.logError("Client token read failed:", e);
                }
            }

            if (s != null) {
                try {
                    clientUUID = UUID.fromString(s);
                } catch (IllegalArgumentException e) {
                    Logger.logError("Client token read failed", e);
                    clientUUID = createUUID();
                }
            } else {
                clientUUID = createUUID();
            }
            return clientUUID;
        }
    }

    public static void setClientToken(UUID u) {
        File tokenFile = new File(getCacheStorageLocation() + File.separator + "clientToken");
        try {
            FileUtils.writeStringToFile(tokenFile, u.toString());
        } catch (IOException e) {
            Logger.logError("Client token write failed", e);
        }
    }

    private static UUID createUUID() {
        UUID u = UUID.randomUUID();
        setClientToken(u);
        return u;
    }

    /**
     *
     * @return pid of the running process. -1 if fail
     */
    public static long getPID() {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        String pid = name.split("@")[0];
        long numericpid = -1;
        try {
            numericpid = Long.parseLong(pid);
        } catch (Exception e) {
            numericpid = -1;
            Logger.logDebug("failed", e);
        }
        return numericpid;
    }

    public static long getPID(Process process) {
        // windows
        if (process.getClass().getName().equals("java.lang.Win32Process")
                || process.getClass().getName().equals("java.lang.ProcessImpl")) {
            long pid = -1;
            try {
                Field f = process.getClass().getDeclaredField("handle");
                f.setAccessible(true);
                pid = Kernel32.INSTANCE.GetProcessId((Long) f.get(process));

            } catch (Exception e) {
                pid = -1;
                Logger.logDebug("failed", e);
            }

            return pid;
        }

        if (process.getClass().getName().equals("java.lang.UNIXProcess")) {
            /* get the PID on unix/linux systems */
            long pid = -1;
            try {
                Field f = process.getClass().getDeclaredField("pid");
                f.setAccessible(true);
                pid = f.getInt(process);

            } catch (Throwable e) {
                pid = -1;
                Logger.logDebug("failed", e);
            }
            return pid;
        }

        Logger.logWarn("Unable to find getpid implementation");
        return -1;
    }

    public static boolean genThreadDump(long pid) {
        if (OSUtils.getCurrentOS() == OS.WINDOWS) {
            // TODO: implement
            Logger.logError("Not implemented yet / Might fail");
            try {
                Runtime runtime = Runtime.getRuntime();
                runtime.exec(new String[] { "sendsignal.exe", Long.toString(pid) });
            } catch (Exception e) {
                Logger.logError(
                        "Failed. You need to install sendsignal.exe in your path to eanble this functionality");
                Logger.logError("Failed", e);
                return false;
            }
            return true;
        } else if (OSUtils.getCurrentOS() == OS.UNIX || OSUtils.getCurrentOS() == OS.MACOSX) {
            Runtime runtime = Runtime.getRuntime();
            try {
                runtime.exec(new String[] { "kill", "-3", Long.toString(pid) });
            } catch (Exception e) {
                Logger.logError("Failed", e);
                return false;
            }
            return true;
        } else {
            Logger.logError("Unable to find genThreadDump implementation");
            return false;
        }
    }

    static interface Kernel32 extends Library {
        public static Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);

        public int GetProcessId(Long hProcess);
    }
}