org.lpe.common.util.system.LpeSystemUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.lpe.common.util.system.LpeSystemUtils.java

Source

/**
 * Copyright 2014 SAP AG
 *
 * 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 org.lpe.common.util.system;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.io.FileUtils;
import org.lpe.common.util.LpeFileUtils;
import org.lpe.common.util.LpeStreamUtils;
import org.lpe.common.util.LpeStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class for generic system operations.
 * 
 * @author Alexander Wert, Roozbeh Farahbod
 * 
 */
public final class LpeSystemUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(LpeSystemUtils.class);

    private static final String NATIVE_SUBFOLDER = "native";

    private static final String JAVA_LIBRARY_PATH = "java.library.path";

    private static final String KILLSERVICE_CMD_WINDOWS = "net stop ";

    private static ExecutorService threadPool;

    private static Logger logger = LoggerFactory.getLogger(LpeSystemUtils.class);

    private static boolean nativeLibrariesLoaded = false;

    private static String systemTempDir = null;

    private static String eol;

    private static OSType os;

    /**
     * private constructor due to utility class.
     */
    private LpeSystemUtils() {
    }

    /**
     * @return a system independent EOL string.
     */
    public static String getEOL() {
        if (eol == null) {
            eol = System.getProperty("line.separator");
            if (eol == null) {
                eol = "\n";
            }
        }
        return eol;
    }

    /**
     * 
     * @return returns the root folder of the running application.
     */
    public static String getRootFolder() {
        return getRootFolder(null);
    }

    /**
     * Submits a task for concurrent execution.
     * 
     * @param command
     *            runnable to execute
     * @return a future object representing the concurrent task
     */
    public static Future<?> submitTask(Runnable command) {
        return getThreadPool().submit(command);
    }

    /**
     * retrieves a cached thread pool
     * 
     * @return thread pool
     */
    private static synchronized ExecutorService getThreadPool() {
        if (threadPool == null) {
            threadPool = Executors.newCachedThreadPool();
        }
        return threadPool;
    }

    /**
     * Detects and returns the root folder of the running application using the
     * passed class to locate the directory.
     * 
     * @param mainClass
     *            class used to locate directory
     * @return returns the root folder of the running application.
     */
    public static String getRootFolder(Class<?> mainClass) {
        if (mainClass == null) {
            mainClass = LpeSystemUtils.class;
        }

        final String baseErrorMsg = "Cannot locate root folder.";

        final String classFile = mainClass.getName().replaceAll("\\.", "/") + ".class";
        final URL classURL = ClassLoader.getSystemResource(classFile);

        String fullPath = "";
        String sampleClassFile = "/org/sopeco/util/Tools.class";
        if (classURL == null) {
            LpeSystemUtils tempObject = new LpeSystemUtils();
            fullPath = tempObject.getClass().getResource(sampleClassFile).toString();
            // logger.warn("{} The application may be running in an OSGi container.",
            // baseErrorMsg);
            // return ".";
        } else {
            fullPath = classURL.toString();
        }

        try {
            fullPath = URLDecoder.decode(fullPath, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            LOGGER.warn("{} UTF-8 encoding is not supported.", baseErrorMsg);
            return ".";
        }

        if (fullPath.indexOf("file:") > -1) {
            fullPath = fullPath.replaceFirst("file:", "").replaceFirst(classFile, "");
            fullPath = fullPath.substring(0, fullPath.lastIndexOf('/'));
        }
        if (fullPath.indexOf("jar:") > -1) {
            fullPath = fullPath.replaceFirst("jar:", "").replaceFirst("!" + classFile, "");
            fullPath = fullPath.substring(0, fullPath.lastIndexOf('/'));
        }
        if (fullPath.indexOf("bundleresource:") > -1) {
            fullPath = fullPath.substring(0, fullPath.indexOf(sampleClassFile));
        }

        // replace the java separator with the
        fullPath = fullPath.replace('/', File.separatorChar);

        // remove leading backslash
        if (fullPath.startsWith("\\")) {
            fullPath = fullPath.substring(1);
        }

        // remove the final 'bin'
        final int binIndex = fullPath.indexOf(File.separator + "bin");
        if (binIndex == fullPath.length() - "/bin".length()) {
            fullPath = fullPath.substring(0, binIndex);
        }

        LOGGER.debug("Root folder is detected at {}.", fullPath);

        return fullPath;
    }

    /**
     * Returns the index of the given object in the given array if it exsists.
     * It uses the {@link Object#equals(Object)} method.
     * 
     * @param obj
     *            object to look for
     * @param array
     *            array of objects
     * @return the index of the object or -1 if the object doesn't exist
     */
    public static int exists(Object obj, Object[] array) {
        for (int i = 0; i < array.length; i++) {
            if (array[i].equals(obj)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * @return the name of the operating system
     */
    public static String getOSName() {
        return System.getProperty("os.name");
    }

    /**
     * @return true if the OS is Mac.
     */
    public static boolean isMacOS() {
        return System.getProperty("os.name").toLowerCase().startsWith("mac");
    }

    /**
     * @return trus if the OS is Windows.
     */
    public static boolean isWindowsOS() {
        return System.getProperty("os.name").toLowerCase().startsWith("windows");
    }

    /**
     * @return trus if the OS is Unix or Linux.
     */
    public static boolean isUnix() {
        final String name = System.getProperty("os.name").toLowerCase();
        return name.contains("nix") || name.contains("nux");
    }

    /**
     * Shutdowns the service identified with the given name.
     * 
     * @param serviceName
     *            the name of the service
     * @throws SystemToolsException
     *             if shutdown fails.
     */
    public static void shutdownService(String serviceName) throws SystemToolsException {
        if (LpeSystemUtils.isWindowsOS()) {
            try {
                Runtime.getRuntime().exec(KILLSERVICE_CMD_WINDOWS + serviceName);
            } catch (IOException e) {
                throw new SystemToolsException("Could not shutdown " + serviceName + ".", e, logger);
            }
        } else {
            logger.warn(
                    "Process is running on a non-Windows OS. Shutting down services is not implemented for this OS.");
            // TODO Implement shutdown for other OSs
        }
    }

    /**
     * Unpacks all native libraries in the JAR files into a temp folder and
     * explicitly appends them to the Java native library path.
     * 
     * The native library files are assumed to be under
     * {@value SystemTools#NATIVE_SUBFOLDER} directories in the classpath. If
     * this is done once, it will not do it again. Also, see
     * {@link #appendLibraryPath(String)}.
     * 
     */
    public static void loadNativeLibraries() {
        if (nativeLibrariesLoaded) {
            logger.warn("Native libraries are already loaded. Nothing done.");
            return;
        }

        try {
            String tempLibDir = extractFilesFromClasspath(NATIVE_SUBFOLDER, "lpeLibs", "native libraries",
                    LpeSystemUtils.class.getClassLoader());
            appendLibraryPath(tempLibDir);

            // trick to make the JVM reload the java.library.path
            Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
            fieldSysPath.setAccessible(true);
            fieldSysPath.set(null, null);

            nativeLibrariesLoaded = true;

            logger.debug("The value of '{}' is {}.", JAVA_LIBRARY_PATH, System.getProperty(JAVA_LIBRARY_PATH));

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Returns the system temp directory.
     * 
     * @return the system temp directory
     */
    public static String getSystemTempDir() {
        if (systemTempDir == null) {
            systemTempDir = FileUtils.getTempDirectoryPath();
            logger.debug("Temp direcotry is located at {}.", systemTempDir);
        }
        return systemTempDir;
    }

    /**
     * Extracts files from a directory in the classpath to a temp directory
     * (with a time stamp) and returns the File instance of the destination
     * directory.
     * 
     * @param srcDirName
     *            a directory name in the classpath
     * @param destName
     *            the name of the destination folder in the temp folder
     * @param fileType
     *            a string describing the file types, if a log message is needed
     * @param classLoader
     *            classloader to use
     * 
     * @return the name of the target directory
     * 
     * @throws IOException
     *             ...
     * @throws URISyntaxException
     *             ...
     * 
     * @see #extractFilesFromClasspath(String, String, String, boolean)
     */
    public static String extractFilesFromClasspath(String srcDirName, String destName, String fileType,
            ClassLoader classLoader) throws IOException, URISyntaxException {
        return extractFilesFromClasspath(srcDirName, destName, fileType, classLoader, true);
    }

    /**
     * Extracts files from a directory in the classpath to a temp directory and
     * returns the File instance of the destination directory.
     * 
     * @param srcDirName
     *            a directory name in the classpath
     * @param destName
     *            the name of the destination folder in the temp folder
     * @param fileType
     *            a string describing the file types, if a log message is needed
     * @param timeStamp
     *            if <code>true</code>, it will add a time stamp to the name of
     *            the target directory (recommended)
     * @param classloader
     *            classloader to use
     * 
     * @return the name of the target directory
     * 
     * @throws IOException
     *             ...
     * @throws URISyntaxException
     *             ...
     */
    public static String extractFilesFromClasspath(String srcDirName, String destName, String fileType,
            ClassLoader classloader, boolean timeStamp) throws IOException, URISyntaxException {

        if (timeStamp) {
            // remove dots and colons from timestamp as they are not allowed for
            // windows directory names
            String clearedTimeStamp = (LpeStringUtils.getTimeStamp() + "-" + Thread.currentThread().getId())
                    .replace('.', '_');

            clearedTimeStamp = clearedTimeStamp.replace(':', '_');
            clearedTimeStamp = clearedTimeStamp.replace(' ', '_');

            destName = destName + "_" + clearedTimeStamp;
        }
        final String targetDirName = LpeFileUtils.concatFileName(getSystemTempDir(), destName);

        // create a temp lib directory
        File targetDirFile = new File(targetDirName);
        if (!targetDirFile.exists()) {
            boolean ok = targetDirFile.mkdir();
            if (!ok) {
                logger.warn("Could not create directory {}", targetDirFile.getAbsolutePath());
            }
        }

        logger.debug("Copying {} to {}.", fileType, targetDirName);

        Enumeration<URL> urls = classloader.getResources(srcDirName); // getSystemResources(srcDirName);

        if (urls.hasMoreElements()) {
            logger.debug("There are some urls for resource '{}' provided by the classloader.", srcDirName);
        } else {
            logger.debug("There are no urls for resource '{}' provided by the classloader.", srcDirName);
        }

        while (urls.hasMoreElements()) {

            final URL url = urls.nextElement();

            if (fileType != null && fileType.trim().length() > 0) {
                logger.debug("Loading {} from {}...", fileType, url);
            }

            Iterator<File> libs = null;
            String unpackedJarDir = null;
            if (url.getProtocol().equals("bundleresource")) {
                continue;
            } else if (url.getProtocol().equals("jar")) {
                try {
                    unpackedJarDir = LpeFileUtils.concatFileName(targetDirName, "temp");

                    extractJARtoTemp(url, srcDirName, unpackedJarDir);

                    final String unpackedNativeDir = LpeFileUtils.concatFileName(unpackedJarDir, srcDirName);
                    final File unpackedNativeDirFile = new File(unpackedNativeDir);
                    libs = FileUtils.iterateFiles(unpackedNativeDirFile, null, false);
                } catch (IllegalArgumentException iae) {
                    // ignore
                    LOGGER.warn("Jar not found, could not extract jar. {}", iae);

                }
            } else {
                // File nativeLibDir = new
                // File(url.toString().replaceAll("\\\\", "/"));
                File nativeLibDir = new File(url.toURI());
                libs = FileUtils.iterateFiles(nativeLibDir, null, false);
            }

            while (libs != null && libs.hasNext()) {
                final File libFile = libs.next();
                logger.debug("Copying resouce file {}...", libFile.getName());
                FileUtils.copyFileToDirectory(libFile, targetDirFile);
            }

            // clean up temp dir
            if (unpackedJarDir != null) {
                File dirToRemove = new File(unpackedJarDir);
                if (dirToRemove.isDirectory() && dirToRemove.exists()) {
                    LpeFileUtils.removeDir(unpackedJarDir);
                }
            }

        }

        return targetDirName;
    }

    /**
     * Extracts a file/folder identified by the URL that resides in the
     * classpath, into the destiation folder.
     * 
     * @param url
     *            URL of the JAR file
     * @param dirOfInterest
     *            the name of the directory of interest
     * @param dest
     *            destination folder
     * 
     * @throws IOException
     *             ...
     * @throws URISyntaxException
     *             ...
     */
    public static void extractJARtoTemp(URL url, String dirOfInterest, String dest)
            throws IOException, URISyntaxException {
        if (!url.getProtocol().equals("jar")) {
            throw new IllegalArgumentException("Cannot locate the JAR file.");
        }

        // create a temp lib directory
        File tempJarDirFile = new File(dest);
        if (!tempJarDirFile.exists()) {
            boolean ok = tempJarDirFile.mkdir();
            if (!ok) {
                logger.warn("Could not create directory {}", tempJarDirFile.getAbsolutePath());
            }

        } else {
            FileUtils.cleanDirectory(tempJarDirFile);
        }

        String urlStr = url.getFile();
        // if (urlStr.startsWith("jar:file:") || urlStr.startsWith("jar:") ||
        // urlStr.startsWith("file:")) {
        // urlStr = urlStr.replaceFirst("jar:", "");
        // urlStr = urlStr.replaceFirst("file:", "");
        // }
        if (urlStr.contains("!")) {
            final int endIndex = urlStr.indexOf("!");
            urlStr = urlStr.substring(0, endIndex);
        }

        URI uri = new URI(urlStr);

        final File jarFile = new File(uri);

        logger.debug("Unpacking jar file {}...", jarFile.getAbsolutePath());

        java.util.jar.JarFile jar = null;
        InputStream is = null;
        OutputStream fos = null;
        try {
            jar = new JarFile(jarFile);
            java.util.Enumeration<JarEntry> entries = jar.entries();
            while (entries.hasMoreElements()) {
                java.util.jar.JarEntry file = (JarEntry) entries.nextElement();

                String destFileName = dest + File.separator + file.getName();
                if (destFileName.indexOf(dirOfInterest + File.separator) < 0
                        && destFileName.indexOf(dirOfInterest + "/") < 0) {
                    continue;
                }

                logger.debug("unpacking {}...", file.getName());

                java.io.File f = new java.io.File(destFileName);
                if (file.isDirectory()) { // if its a directory, create it
                    boolean ok = f.mkdir();
                    if (!ok) {
                        logger.warn("Could not create directory {}", f.getAbsolutePath());
                    }
                    continue;
                }
                is = new BufferedInputStream(jar.getInputStream(file));
                fos = new BufferedOutputStream(new FileOutputStream(f));

                LpeStreamUtils.pipe(is, fos);
            }

            logger.debug("Unpacking jar file done.");
        } catch (IOException e) {
            throw e;
        } finally {
            if (jar != null) {
                jar.close();
            }
            if (fos != null) {
                fos.close();
            }
            if (is != null) {
                is.close();
            }
        }
    }

    /**
     * Appends a directory to the Java library path.
     * 
     * @param path
     *            the new path
     * @throws Exception
     *             ...
     * @see {@link #setLibraryPath(String)}
     */
    public static void appendLibraryPath(String path) throws Exception {
        setLibraryPath(System.getProperty(JAVA_LIBRARY_PATH) + File.pathSeparator + path);
    }

    /**
     * Resets the Java library path to a new value.
     * 
     * @param path
     *            the new path
     * @throws Exception
     *             ...
     */
    public static void setLibraryPath(String path) throws Exception {
        System.setProperty(JAVA_LIBRARY_PATH, path);

        // set sys_paths to null
        final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
        sysPathsField.setAccessible(true);
        sysPathsField.set(null, null);
    }

    /**
     * Main method. Load native libraries.
     * 
     * @param args
     *            not used
     */
    public static void main(String[] args) {
        loadNativeLibraries();
    }

    /**
     * Determines the operating system.
     * 
     * @return operating system
     */
    public static OSType getOperatingSystemType() {
        if (os == null) {
            String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
            if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) {
                os = OSType.MAC;
            } else if (OS.indexOf("win") >= 0) {
                os = OSType.WINDOWS;
            } else if (OS.indexOf("nux") >= 0) {
                os = OSType.LINUX;
            }
        }
        return os;
    }
}