JStackInventoryPrinter.java 6.33 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  org.apache.felix.inventory.Format
 *  org.apache.felix.inventory.InventoryPrinter
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Deactivate
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Service
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.granite.threaddump.impl;

import com.adobe.granite.threaddump.impl.LoggerWriter;
import com.adobe.granite.threaddump.impl.StreamGobbler;
import java.io.File;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.felix.inventory.Format;
import org.apache.felix.inventory.InventoryPrinter;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(name="com.adobe.granite.threaddump.JStackInventoryPrinter")
@Service(value={InventoryPrinter.class})
@Properties(value={@Property(name="felix.inventory.printer.name", value={"jstack-threaddump"}), @Property(name="felix.inventory.printer.title", value={"Threads (via JStack)"})})
public final class JStackInventoryPrinter
implements InventoryPrinter {
    private static final Pattern JVM_NAME_PATTERN = Pattern.compile("(\\d+)@.+");
    private static final Pattern JVM_VERSION_PATTERN = Pattern.compile("1\\.[78]\\..*");
    private static final String OS_NAME_SYSTEM_PROPERTY = "os.name";
    private static final String JAVA_HOME_SYSTEM_PROPERTY = "java.home";
    private static final String JAVA_VERSION_SYSTEM_PROPERTY = "java.version";
    private static final String JAVA_HOME_ENV_PROPERTY = "JAVA_HOME";
    private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
    private static final String OS_NAME_MACOSX_PREFIX = "Mac OS X";
    private final Logger logger;
    private String pid;
    private File jStackExecutable;

    public JStackInventoryPrinter() {
        this.logger = LoggerFactory.getLogger(this.getClass());
    }

    @Activate
    protected void activate() {
        this.pid = JStackInventoryPrinter.getPid();
        File javaHome = JStackInventoryPrinter.getJavaHome();
        if (javaHome == null || !javaHome.exists() || !javaHome.isDirectory()) {
            this.logger.warn("There is no way to access to current Java home, please check `java.home` System Property or `JAVA_HOME` environment variable.");
        } else {
            this.jStackExecutable = JStackInventoryPrinter.getJStackExecutable(javaHome);
        }
    }

    @Deactivate
    protected void deactivate() {
        this.pid = null;
        this.jStackExecutable = null;
    }

    public void print(PrintWriter printWriter, Format format, boolean isZip) {
        if (this.pid == null) {
            this.logger.warn("There is no way to access to this current process PID, `jstack` cannot be executed.");
            return;
        }
        if (this.jStackExecutable == null || !this.jStackExecutable.exists() || !this.jStackExecutable.isFile()) {
            this.logger.warn("There is no way to access to the `jstack` command, please check it is correctly installed in the JDK");
            return;
        }
        try {
            Process process = Runtime.getRuntime().exec(new String[]{this.jStackExecutable.getAbsolutePath(), this.pid});
            Thread inputStreamGlobber = StreamGobbler.redirect(process.getInputStream(), printWriter);
            Thread errorStreamGlobber = StreamGobbler.redirect(process.getErrorStream(), new LoggerWriter(this.logger));
            int exitValue = process.waitFor();
            inputStreamGlobber.join();
            errorStreamGlobber.join();
            if (exitValue != 0) {
                this.logger.error("An error occurred while executing {} {}, process exited with code: {}", new Object[]{this.jStackExecutable, this.pid, exitValue});
            }
        }
        catch (Exception e) {
            this.logger.error("An error occurred while executing {} {}, see root errors: {}", new Object[]{this.jStackExecutable, this.pid, e});
        }
    }

    private static String getPid() {
        String jvm = ManagementFactory.getRuntimeMXBean().getName();
        Matcher matcher = JVM_NAME_PATTERN.matcher(jvm);
        if (matcher.matches()) {
            return matcher.group(1);
        }
        return null;
    }

    private static File getJavaHome() {
        File javaHome = null;
        String javaHomeProperty = System.getProperty("java.home");
        if (javaHomeProperty != null && javaHomeProperty.length() > 0) {
            javaHome = new File(javaHomeProperty);
        }
        if (!(javaHome != null && javaHome.exists() && javaHome.isDirectory() || (javaHomeProperty = System.getenv("JAVA_HOME")) == null || javaHomeProperty.length() <= 0)) {
            javaHome = new File(javaHomeProperty);
        }
        return javaHome;
    }

    private static boolean isOs(String namePrefix) {
        String osName = System.getProperty("os.name");
        if (osName.startsWith(namePrefix)) {
            return true;
        }
        return false;
    }

    private static boolean isRecentJvmVersion() {
        String javaVersion = System.getProperty("java.version");
        return JVM_VERSION_PATTERN.matcher(javaVersion).matches();
    }

    private static /* varargs */ File getFile(File parent, String ... path) {
        File tmp = parent;
        for (String current : path) {
            tmp = new File(tmp, current);
        }
        return tmp;
    }

    private static File getJStackExecutable(File javaHome) {
        String jstackCommand = "jstack";
        if (JStackInventoryPrinter.isOs("Windows")) {
            jstackCommand = jstackCommand + ".exe";
        }
        if (JStackInventoryPrinter.isOs("Mac OS X") && !JStackInventoryPrinter.isRecentJvmVersion()) {
            return JStackInventoryPrinter.getFile(javaHome, "bin", jstackCommand);
        }
        return JStackInventoryPrinter.getFile(javaHome, "..", "bin", jstackCommand);
    }
}