Package org.jahia.tools.jvm

Source Code of org.jahia.tools.jvm.ThreadMonitor$ThreadDumpTask

/**
* This file is part of Jahia, next-generation open source CMS:
* Jahia's next-generation, open source CMS stems from a widely acknowledged vision
* of enterprise application convergence - web, search, document, social and portal -
* unified by the simplicity of web content management.
*
* For more information, please visit http://www.jahia.com.
*
* Copyright (C) 2002-2011 Jahia Solutions Group SA. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception to the terms and conditions of version 2.0 of
* the GPL (or any later version), you may redistribute this Program in connection
* with Free/Libre and Open Source Software ("FLOSS") applications as described
* in Jahia's FLOSS exception. You should have received a copy of the text
* describing the FLOSS exception, and it is also available here:
* http://www.jahia.com/license
*
* Commercial and Supported Versions of the program (dual licensing):
* alternatively, commercial and supported versions of the program may be used
* in accordance with the terms and conditions contained in a separate
* written agreement between you and Jahia Solutions Group SA.
*
* If you are unsure which license is appropriate for your use,
* please contact the sales department at sales@jahia.com.
*/

package org.jahia.tools.jvm;

import static java.lang.management.ManagementFactory.*;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.lang.management.ThreadInfo;
import java.util.Date;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

import javax.management.*;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.jahia.bin.errors.ErrorFileDumper;
import org.jahia.bin.listeners.JahiaContextLoaderListener;
import org.jahia.settings.SettingsBean;

import java.io.*;

/**
* Warning : generating thread dumps is an operation that locks the JVM and therefore should not be done while
* high load is running on the system.
*/
public class ThreadMonitor {
   
    private static class ThreadDumpTask extends TimerTask {

        private static void out(String msg) {
            System.out.println(msg);
        }
        private int executionCount;
        private int numberOfExecutions;
        private File targetFile;
        private Timer timer;

        private boolean toSystemOut;

        ThreadDumpTask(Timer timer, int numberOfExecutions, boolean toSystemOut, File targetFile) {
            super();
            this.numberOfExecutions = numberOfExecutions;
            this.targetFile = targetFile;
            this.timer = timer;
            this.toSystemOut = toSystemOut;
            out("Starting thread dump task for " + numberOfExecutions + " executions into a file " + targetFile);
        }

        @Override
        public void run() {
            executionCount++;
            if (executionCount > numberOfExecutions) {
                return;
            }
            out("Executing thread dump " + executionCount + " of " + numberOfExecutions);
            OutputStream out = null;
            try {
                String dump = new ThreadMonitor().getFullThreadInfo();
                if (toSystemOut) {
                    System.out.println(dump);
                }
                if (targetFile != null) {
                    out = new FileOutputStream(targetFile, true);
                    out.write(new ThreadMonitor().getFullThreadInfo().getBytes("UTF-8"));
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (targetFile != null) {
                    IOUtils.closeQuietly(out);
                }
                if (executionCount >= numberOfExecutions) {
                    timer.cancel();
                    out("Stopping thread dump task after " + executionCount + " executions into a file " + targetFile);
                }
            }
        }
    }

    private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
   
    private static final String DUMP_END ="\n<EndOfDump>\n\n";

    private static String INDENT = "    ";
    private static File getNextThreadDumpFile(String postfix) {
        Date now = new Date();
        File todaysDirectory = new File(new File(System.getProperty("java.io.tmpdir"),
                "jahia-threads"), ErrorFileDumper.DATE_FORMAT_DIRECTORY.format(now));
        todaysDirectory.mkdirs();
        return new File(todaysDirectory, "thread-dump-"
                + ErrorFileDumper.DATE_FORMAT_FILE.format(now) + (postfix != null ? postfix : "")
                + ".out");
    }

    private String dumpPrefix = "\nFull thread dump ";
   
    private MBeanServerConnection server;

    private ThreadMXBean tmbean;

    /**
     * Constructs a ThreadMonitor object to get thread information in the local JVM.
     */
    public ThreadMonitor() {
        this(getPlatformMBeanServer());
    }

    /**
     * Constructs a ThreadMonitor object to get thread information in a remote JVM.
     */
    public ThreadMonitor(MBeanServerConnection server) {
        setMBeanServerConnection(server);
        try {
            parseMBeanInfo();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Prints the thread dump information to System.out.
     */
    public void dumpThreadInfo() {
        dumpThreadInfo(true, false);
    }

    /**
     * Prints the thread dump information to System.out or/and to a file.
     * @param toSysOut print the generated thread dump to a System.out
     * @param toFile print the generated thread dump to a file
     */
    public void dumpThreadInfo(boolean toSysOut, boolean toFile) {
        if (!(toSysOut || toFile)) {
            return;
        }
       
        String threadInfo = getFullThreadInfo();
        if (toSysOut) {
            System.out.print(threadInfo);
        }
       
        if (toFile) {
            final File dumpFile = getNextThreadDumpFile(null);
            try {
                FileUtils.writeStringToFile(dumpFile, threadInfo, "UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void dumpThreadInfo(StringBuilder dump) {
        dump.append(getDumpDate());
        dump.append(dumpPrefix);
        dump.append("\n");
        long[] tids = tmbean.getAllThreadIds();
        ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);
        for (int i = 0; i < tinfos.length; i++) {
            ThreadInfo ti = tinfos[i];
            printThreadInfo(ti, dump);
        }
        dump.append(DUMP_END);
    }

    private void dumpThreadInfoUsingJstack(StringBuilder dump) {
        try {
            Process p = Runtime.getRuntime().exec("jstack " + JahiaContextLoaderListener.getPid());
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            for (String line = br.readLine(); line != null; line = br.readLine()) {
                dump.append(line).append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Starts a background thread to do series of thread dumps with the specified interval.
     *
     * @param toSysOut
     *            print the generated thread dump to a System.out
     * @param toFile
     *            print the generated thread dump to a file
     * @param threadDumpCount
     *            the number of thread dumps to do
     * @param intervalSeconds
     *            the interval between thread dumps in seconds
     */
    public void dumpThreadInfoWithInterval(boolean toSysOut, boolean toFile, int threadDumpCount,
            int intervalSeconds) {
        if (threadDumpCount < 1 || intervalSeconds < 1 || !(toSysOut || toFile)) {
            return;
        }
       
        Timer timer = new Timer(true);
        File file = toFile ? getNextThreadDumpFile("-" + threadDumpCount + "-executions") : null;
        timer.schedule(new ThreadDumpTask(timer, threadDumpCount, toSysOut, file), 0,
                intervalSeconds * 1000L);
    }

    /**
     * Checks if any threads are deadlocked. If any, print the thread dump information.
     */
    public String findDeadlock() {
        StringBuilder dump = new StringBuilder();
        long[] tids = tmbean.findMonitorDeadlockedThreads();
        if (tids == null) {
            return null;
        }
        dump.append("\n\nFound one Java-level deadlock:\n");
        dump.append("==============================\n");
        ThreadInfo[] infos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);
        for (int i = 1; i < infos.length; i++) {
            ThreadInfo ti = infos[i];
            // print thread information
            printThreadInfo(ti, dump);
        }

        return (dump.toString());
    }

    /**
     * Generates a string with the full thread dump information.
     *
     * @param writer the output writer
     */
    public void generateThreadInfo(Writer writer) {
        try {
            writer.write(getFullThreadInfo());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * create dump date similar to format used by 1.6 VMs
     *
     * @return dump date (e.g. 2007-10-25 08:00:00)
     */
    private String getDumpDate() {
        return (DATE_FORMAT.format(new Date()));
    }

    /**
     * Generates a string with the full thread dump information.
     *
     * @return the thread dump content as string
     */
    public String getFullThreadInfo() {
        StringBuilder dump = new StringBuilder(65536);

        if (SettingsBean.getInstance().isUseJstackForThreadDumps()) {
            dumpThreadInfoUsingJstack(dump);
        } else {
            dumpThreadInfo(dump);
        }
       
        return dump.toString();
    }

    private void parseMBeanInfo() throws IOException {
            setDumpPrefix();
    }

    private void printThread(ThreadInfo ti, StringBuilder dump) {
        StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\""
                + " nid=" + ti.getThreadId() + " state=" + ti.getThreadState());
        if (ti.isSuspended()) {
            sb.append(" (suspended)");
        }
        if (ti.isInNative()) {
            sb.append(" (running in native)");
        }
        sb.append(" []\n"
                + ti.getThreadState().getClass().getName().replace("$", ".")
                + ": " + ti.getThreadState());
        if (ti.getLockName() != null
                && ti.getThreadState() != Thread.State.BLOCKED) {
            String[] lockInfo = ti.getLockName().split("@");
            sb.append("\n" + INDENT + "- waiting on <0x" + lockInfo[1]
                    + "> (a " + lockInfo[0] + ")");
            sb.append("\n" + INDENT + "- locked <0x" + lockInfo[1] + "> (a "
                    + lockInfo[0] + ")");
        } else if (ti.getLockName() != null
                && ti.getThreadState() == Thread.State.BLOCKED) {
            String[] lockInfo = ti.getLockName().split("@");
            sb.append("\n" + INDENT + "- waiting to lock <0x" + lockInfo[1]
                    + "> (a " + lockInfo[0] + ")");
        }
        dump.append(sb.toString());
        dump.append("\n");
        if (ti.getLockOwnerName() != null) {
            dump.append(INDENT + " owned by " + ti.getLockOwnerName() + " id="
                    + ti.getLockOwnerId());
            dump.append("\n");
        }
    }

    private void printThreadInfo(ThreadInfo ti, StringBuilder dump) {
        // print thread information
        printThread(ti, dump);

        // print stack trace with locks
        StackTraceElement[] stacktrace = ti.getStackTrace();
        for (int i = 0; i < stacktrace.length; i++) {
            StackTraceElement ste = stacktrace[i];
            dump.append(INDENT + "at " + ste.toString());
            dump.append("\n");
        }
        dump.append("\n");
    }

    private void setDumpPrefix() {
        try {
            RuntimeMXBean rmbean = (RuntimeMXBean) ManagementFactory
                    .newPlatformMXBeanProxy(server,
                            ManagementFactory.RUNTIME_MXBEAN_NAME,
                            RuntimeMXBean.class);
            dumpPrefix += rmbean.getVmName() + " (" + rmbean.getVmVersion()
                    + ")\n";
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * reset mbean server connection
     *
     * @param mbs
     */
    void setMBeanServerConnection(MBeanServerConnection mbs) {
        this.server = mbs;
        try {
            this.tmbean = (ThreadMXBean) ManagementFactory
                    .newPlatformMXBeanProxy(server,
                            ManagementFactory.THREAD_MXBEAN_NAME,
                            ThreadMXBean.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
TOP

Related Classes of org.jahia.tools.jvm.ThreadMonitor$ThreadDumpTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.