org.fluentd.jvmwatcher.LocalJvmInfo.java Source code

Java tutorial

Introduction

Here is the source code for org.fluentd.jvmwatcher.LocalJvmInfo.java

Source

//
// A Java VM status Watcher for Fluent
//
// Copyright (C) 2013 Masayuki Miyake
//
//    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.fluentd.jvmwatcher;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;

import sun.management.ConnectorAddressLink;
import sun.jvmstat.monitor.HostIdentifier;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.MonitorException;
import sun.jvmstat.monitor.VmIdentifier;

/**
 * Local Java VM Information class.
 * @author miyake
 *
 */
public class LocalJvmInfo {
    private static Log log = LogFactory.getLog(LocalJvmInfo.class);

    /**
     * JVM connect address
     */
    private String mbeanAddress_ = null;
    /**
     * Java program command line 
     */
    private String commandLine_ = null;
    /**
     * Java program display name 
     */
    private String displayName_ = null;
    /**
     * Java VM ID (pid)
     */
    private int jvmId_ = -1;
    /**
     * The short process name for the log output.
     */
    private String shortName_ = null;
    /**
     * is JVM attach supported
     */
    private boolean isAttachSupported_ = false;

    /**
     * Local JVM connect address.
     */
    private static final String LOCAL_CONNECTOR_ADDRESS_PROP = "com.sun.management.jmxremote.localConnectorAddress";

    /**
     * 
     */
    private static Pattern myProcNamePattern_ = Pattern.compile("(org\\.fluentd\\.jvmwatcher\\.JvmWatcher)");

    /**
     * 
     */
    private static Map<String, Pattern> filterPassProcMap_ = new HashMap<String, Pattern>();

    /**
     * Constructor
     * 
     * @param vmid
     * @param commandLine
     * @param canAttach
     * @param connectorAddress
     */
    public LocalJvmInfo(int vmid, String commandLine, boolean canAttach, String connectorAddress) {
        this.jvmId_ = vmid;
        this.commandLine_ = commandLine;
        this.isAttachSupported_ = canAttach;
        this.mbeanAddress_ = connectorAddress;
        this.displayName_ = getDisplayName(commandLine);
        this.shortName_ = this.displayName_;
    }

    /**
     * Default Constructor
     */
    private LocalJvmInfo() {

    }

    /**
     * get All local JVM information.
     * 
     * @return
     */
    public static Map<Integer, LocalJvmInfo> getAllLocalJvmInfos() {
        Map<Integer, LocalJvmInfo> map = new HashMap<Integer, LocalJvmInfo>();
        getMonitoredJvms(map);
        getAttachableJvms(map);
        return map;
    }

    /**
     * Monitored Local Java VM get information.
     * @param map
     */
    private static void getMonitoredJvms(Map<Integer, LocalJvmInfo> map) {
        MonitoredHost monHost = null;
        Set activVmsSet = null;

        try {
            // get monitored host jvms
            monHost = MonitoredHost.getMonitoredHost(new HostIdentifier((String) null));
            activVmsSet = monHost.activeVms();
        } catch (java.net.URISyntaxException sx) {
            log.error(sx);
            return;
        } catch (MonitorException mx) {
            log.error(mx);
            return;
        }

        for (Object jvmid : activVmsSet) {
            if (jvmid instanceof Integer) {
                int pid = ((Integer) jvmid).intValue();
                String name = jvmid.toString();
                boolean attachable = false;
                String address = null;
                try {
                    MonitoredVm mvm = monHost.getMonitoredVm(new VmIdentifier(name));
                    // use the command line as the display name
                    name = MonitoredVmUtil.commandLine(mvm);
                    attachable = MonitoredVmUtil.isAttachable(mvm);
                    address = ConnectorAddressLink.importFrom(pid);

                    mvm.detach();
                } catch (Exception ex) {
                    log.error(ex);
                }
                // put LocalJvmInfo
                if (isMyProcess(name) != true) {
                    LocalJvmInfo jvmInfo = new LocalJvmInfo(pid, name, attachable, address);
                    // check target process
                    jvmInfo = isTargetProcess(jvmInfo);

                    if (null != jvmInfo) {
                        map.put(pid, jvmInfo);
                    }
                }
            }
        }
    }

    /**
     * Attachable Local Java VM get information.
     * @param map
     */
    private static void getAttachableJvms(Map<Integer, LocalJvmInfo> map) {
        List<VirtualMachineDescriptor> vms = VirtualMachine.list();
        for (VirtualMachineDescriptor vmd : vms) {
            try {
                Integer vmid = Integer.valueOf(vmd.id());
                if (!map.containsKey(vmid)) {
                    boolean attachable = false;
                    String address = null;
                    try {
                        VirtualMachine vm = VirtualMachine.attach(vmd);
                        attachable = true;
                        Properties agentProps = vm.getAgentProperties();
                        address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
                        vm.detach();
                    } catch (AttachNotSupportedException ex) {
                        // not attachable
                        log.error("get Attachable JVM error", ex);
                    } catch (IOException ioex) {
                        // ignore
                        log.error("get Attachable JVM error", ioex);
                    }
                    // put LocalJvmInfo
                    if (isMyProcess(vmd.displayName()) != true) {
                        LocalJvmInfo jvmInfo = new LocalJvmInfo(vmid, vmd.displayName(), attachable, address);
                        // check target process
                        jvmInfo = isTargetProcess(jvmInfo);

                        if (null != jvmInfo) {
                            map.put(vmid, jvmInfo);
                        }
                    }
                }
            } catch (NumberFormatException numex) {
                // do not support vmid different than pid
                log.error("do not support vmid different than pid.", numex);
            }
        }
    }

    /**
     * CommandLine string convert to display name.
     * @param commandLine
     * @return
     */
    private String getDisplayName(String commandLine) {
        // trim the pathname of jar file if it's a jar
        String[] res = commandLine.split(" ", 2);
        if (res[0].endsWith(".jar")) {
            File jarfile = new File(res[0]);
            String displayName = jarfile.getName();
            if (res.length == 2) {
                displayName += " " + res[1];
            }
            return displayName;
        }
        return commandLine;
    }

    /**
     * @param name
     * @return
     */
    public static boolean isMyProcess(String name) {
        boolean ret = false;

        Matcher match = myProcNamePattern_.matcher(name);

        ret = match.find();

        return ret;
    }

    /**
     * @param shortName
     * @param patt
     */
    public static void addTargetProcessPattern(String shortName, Pattern patt) {
        filterPassProcMap_.put(shortName, patt);
        log.info("add target process. shortname=" + shortName + " match pattern=" + patt.pattern());
    }

    /**
     * @param checkJvm
     * @return
     */
    public static LocalJvmInfo isTargetProcess(LocalJvmInfo checkJvm) {
        LocalJvmInfo ret = null;

        /*
         *  If the process name which is dealt with for the measurement isn't defining, 
         *  it makes a measurement object unconditionally.
         */
        if (filterPassProcMap_.size() == 0) {
            return checkJvm;
        }

        // check process name
        for (Map.Entry<String, Pattern> elem : filterPassProcMap_.entrySet()) {
            String key = elem.getKey();
            Pattern val = elem.getValue();
            Matcher match = val.matcher(checkJvm.displayName_);

            // The process name which is dealt with for the measurement was found out.
            if (match.find()) {
                ret = checkJvm;
                checkJvm.setShortName(key);
                log.debug("match prosecc=" + val.pattern());
                break;
            } else {
                log.debug("unmatch prosecc=" + val.pattern());
            }
        }

        return ret;
    }

    /**
     * Start JVM management agent.
     * @throws IOException
     */
    public void startManagementAgent() throws IOException {
        if (this.mbeanAddress_ != null) {
            // already started
            return;
        }

        if (!this.isAttachSupported_) {
            throw new IOException("This virtual machine \"" + this.jvmId_ + "\" does not support dynamic attach.");
        }

        // load management agent.
        loadManagementAgent();

        // fails to load or start the management agent
        if (this.mbeanAddress_ == null) {
            // should never reach here
            throw new IOException("Fails to find connector address");
        }
    }

    /**
     * Load JVM management agent.
     * @throws IOException
     */
    private void loadManagementAgent() throws IOException {
        VirtualMachine vm = null;
        String name = String.valueOf(this.jvmId_);
        try {
            vm = VirtualMachine.attach(name);
        } catch (AttachNotSupportedException x) {
            IOException ioe = new IOException(x.getMessage());
            ioe.initCause(x);
            throw ioe;
        }

        // create management-agent.jar path
        String home = vm.getSystemProperties().getProperty("java.home");

        // ${java.home}/jre/lib/management-agent.jar or ${java.home}/lib in build environments.
        String agent = home + File.separator + "jre" + File.separator + "lib" + File.separator
                + "management-agent.jar";
        File file = new File(agent);
        if (!file.exists()) {
            agent = home + File.separator + "lib" + File.separator + "management-agent.jar";
            file = new File(agent);
            if (!file.exists()) {
                throw new IOException("Management agent not found");
            }
        }

        agent = file.getCanonicalPath();
        try {
            vm.loadAgent(agent, "com.sun.management.jmxremote");
        } catch (AgentLoadException ex) {
            IOException ioe = new IOException(ex.getMessage());
            ioe.initCause(ex);
            throw ioe;
        } catch (AgentInitializationException ex) {
            IOException ioe = new IOException(ex.getMessage());
            ioe.initCause(ex);
            throw ioe;
        }

        // get the connector address
        Properties agentProps = vm.getAgentProperties();
        this.mbeanAddress_ = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);

        vm.detach();
    }

    /**
     * @return MBean Address
     */
    public String getAddress() {
        return mbeanAddress_;
    }

    /**
     * @return commandLine
     */
    public String getCommandLine_() {
        return commandLine_;
    }

    /**
     * @return displayName
     */
    public String getDisplayName() {
        return displayName_;
    }

    /**
     * @return JavaVM ID
     */
    public int getJvmid() {
        return jvmId_;
    }

    /**
     * @return isAttachSupported
     */
    public boolean isAttachSupported() {
        return isAttachSupported_;
    }

    /**
     * Manageable check
     * @return
     */
    public boolean isManageable() {
        return (this.mbeanAddress_ != null);
    }

    /**
     * set short name.
     * @param name
     */
    public void setShortName(String name) {
        this.shortName_ = name;
    }

    /**
     * get short name.
     * @return
     */
    public String getShortName() {
        return this.shortName_;
    }

    /* (? Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuffer buff = new StringBuffer();

        buff.append("vmid=").append(this.jvmId_).append(" address=").append(this.mbeanAddress_);
        buff.append(" commanLine=").append(this.commandLine_).append(" displayName=").append(this.displayName_);
        buff.append(" isAttachSuppoted=").append(this.isAttachSupported_);

        return buff.toString();
    }
}