public enum JMXUnavailabilityType {
        /**
         * 连接失败
         */
        connectionFailed(0),
        /**
         * 获取JMX值超时
         */
        getMbeanValueTimeout(-1),
        /**
         * 获取JMX值发生异常
         */
        getMbeanValueException(-2),
        /**
         * 获取JMX对象集合超时
         */
        getObjectNameListTimeout(-3),
        /**
         * 获取JMX对象集合发生异常
         */
        getObjectNameListException(-4),
        /**
         * 未知异常
         */
        unKnown(-5),
        ;

        @Getter
        private int type;

        JMXUnavailabilityType(int type) {
            this.type = type;
        }
    } public enum CounterType {
    /**
     * 用户上传什么样的值，就原封不动的存储
     */
    GAUGE,
    /**
     * 指标在存储和展现的时候，会被计算为speed，即（当前值 - 上次值）/ 时间间隔
     */
    COUNTER
} @Data
public class FalconReportObject implements Cloneable{

    /**
     *  标明Metric的主体(属主)，比如metric是cpu_idle，那么Endpoint就表示这是哪台机器的cpu_idle
     */
    private String endpoint;
    /**
     * 最核心的字段，代表这个采集项具体度量的是什么, 比如是cpu_idle呢，还是memory_free, 还是qps
     */
    private String metric;
    /**
     * 表示汇报该数据时的unix时间戳，注意是整数，代表的是秒
     */
    private long timestamp;
    /**
     * 表示该数据采集项的汇报周期，这对于后续的配置监控策略很重要，必须明确指定。
     */
    private int step;
    /**
     * 代表该metric在当前时间点的值,数值类型
     */
    private String value;
    /**
     * 只能是COUNTER或者GAUGE二选一，前者表示该数据采集项为计时器类型，后者表示其为原值 (注意大小写)
     GAUGE：即用户上传什么样的值，就原封不动的存储
     COUNTER：指标在存储和展现的时候，会被计算为speed，即（当前值 - 上次值）/ 时间间隔
     */
    private CounterType counterType;
    /**
     * 一组逗号分割的键值对, 对metric进一步描述和细化, 可以是空字符串. 比如idc=lg，比如service=xbox等，多个tag之间用逗号分割
     */
    private String tags;

    /**
     * 仅供系统使用
     */
    private ObjectName objectName;

    @Override
    public FalconReportObject clone() {
        try {
            return (FalconReportObject) super.clone();
        } catch (CloneNotSupportedException e) {
            return new FalconReportObject();
        }
    }

    /**
     * 添加tag
     * @param newTag
     * @return
     */
    public FalconReportObject appendTags(String newTag){
        if(!StringUtils.isEmpty(newTag)){
            if(StringUtils.isEmpty(this.getTags())){
                this.setTags(newTag);
            }else{
                this.setTags(this.getTags() + "," + newTag);
            }
        }
        return this;
    }

} public enum MetricsType {

    /**
     * 可用性监控值
     */
    AVAILABILITY("availability"),
    /**
     * 配置文件配置的JMX监控值
     */
    JMX_OBJECT_CONF("jmxObjectConf"),
    /**
     * 內建的JMX公共的监控属性
     */
    JMX_OBJECT_IN_BUILD("jmxObjectInBuild"),
    /**
     * 配置文件配置的通过http访问获取的监控值
     */
    HTTP_URL_CONF("httpUrlConf"),
    /**
     * 配置文件配置的SQL语句查询的监控值
     */
    SQL_CONF("sqlConf"),
    /**
     * 內建的SQL语句查询的监控值
     */
    SQL_IN_BUILD("sqlInBuild"),
    /**
     * 內建的公共的SNMP获取的监控值
     */
    SNMP_COMMON_IN_BUILD("snmpCommonInBuild"),
    /**
     * 插件內建的SNMP监控值
     */
    SNMP_Plugin_IN_BUILD("snmpPluginInBuild"),
    DETECT("detect");

    private String typeName;

    MetricsType(String typeName) {
        this.typeName = typeName;
    }

    public String getTypeName() {
        return typeName;
    }
} @Slf4j
public class JMXConnection {
    private static final Map<String,JMXConnectionInfo> connectCacheLibrary = new HashMap<>();//JMX的连接缓存
    private static final Map<String,Integer> serverConnectCount = new HashMap<>();//记录服务应有的JMX连接数

    private String serverName;

    public JMXConnection(String serverName) {
        this.serverName = serverName;
    }

    /**
     * 删除JMX连接池连接
     * @param serverName
     * JMX服务名
     * @param pid
     * 进程id
     */
    public static void removeConnectCache(String serverName,int pid){
        String key = serverName + pid;
        if(connectCacheLibrary.remove(key) != null){
            //删除成功,更新serverConnectCount
            int count = serverConnectCount.get(serverName);
            serverConnectCount.put(serverName,count - 1);
            log.info("已清除JMX监控: {} , pid: {}",serverName,pid);
        }
    }

    /**
     * 根据服务名,返回该服务应有的JMX连接数
     * @param serverName
     * @return
     */
    public static int getServerConnectCount(String serverName){
        return serverConnectCount.get(serverName);
    }

    /**
     * 获取本地是否已开启指定的JMX服务
     * @param serverName
     * @return
     */
    public static boolean hasJMXServerInLocal(String serverName){
        if(!StringUtils.isEmpty(serverName)){
            List<VirtualMachineDescriptor> vms = VirtualMachine.list();
            for (VirtualMachineDescriptor desc : vms) {
                File file = new File(desc.displayName());
                if(file.exists()){
                    //java -jar 形式启动的Java应用
                    if(file.toPath().getFileName().toString().equals(serverName)){
                        return true;
                    }
                }else if(desc.displayName().contains(serverName)){
                    return true;
                }

            }
        }
        return false;
    }

    /**
     *
     * @throws IOException
     */
    public static void closeAll() {
        for (JMXConnectionInfo jmxConnectionInfo : connectCacheLibrary.values()) {
            jmxConnectionInfo.closeJMXConnector();
        }
    }

    /**
     * 获取指定服务名的本地JMX VM 描述对象
     * @param serverName
     * @return
     */
    public static List<VirtualMachineDescriptor> getVmDescByServerName(String serverName){
        List<VirtualMachineDescriptor> vmDescList = new ArrayList<>();
        List<VirtualMachineDescriptor> vms = VirtualMachine.list();
        for (VirtualMachineDescriptor desc : vms) {
            File file = new File(desc.displayName());
            if(file.exists()){
                //java -jar 形式启动的Java应用
                if(file.toPath().getFileName().toString().equals(serverName)){
                    vmDescList.add(desc);
                }
            }else if(desc.displayName().contains(serverName)){
                vmDescList.add(desc);
            }
        }
        return vmDescList;
    }



    /**
     * 获取JMX连接
     * @return
     * @throws IOException
     */
    public synchronized List<JMXConnectionInfo> getMBeanConnection(){
        if(StringUtils.isEmpty(serverName)){
            log.error("获取JMX连接的serverName不能为空");
            return new ArrayList<>();
        }

        List<VirtualMachineDescriptor> vmDescList = getVmDescByServerName(serverName);

        List<JMXConnectionInfo> connections = connectCacheLibrary.entrySet().
                stream().
                filter(entry -> NumberUtils.isNumber(entry.getKey().replace(serverName,""))).
                map(Map.Entry::getValue).
                collect(Collectors.toList());

        if(connections.isEmpty() || connections.size() < vmDescList.size()){ //JMX连接池为空,进行连接获取
            int count = 0;
            connections.clear();
            clearCache();
            for (VirtualMachineDescriptor desc : vmDescList) {
                JMXConnectUrlInfo jmxConnectUrlInfo = getConnectorAddress(desc);
                if (jmxConnectUrlInfo == null) {
                    log.error("应用 {} 的JMX连接URL获取失败",desc.displayName());
                    //对应的ServerName的JMX连接获取失败，返回该服务JMX连接失败，用于上报不可用记录
                    connections.add(initBadJMXConnect(desc));
                    count++;
                    continue;
                }

                try {
                    connections.add(initJMXConnectionInfo(getJMXConnector(jmxConnectUrlInfo),desc));
                    log.debug("应用 {} JMX 连接已建立",serverName);

                } catch (Exception e) {
                    log.error("JMX 连接获取异常:{}",e.getMessage());
                    //JMX连接获取失败，添加该服务JMX的不可用记录，用于上报不可用记录
                    connections.add(initBadJMXConnect(desc));
                }
                //该服务应有的数量++
                count++;
            }

            if(count > 0){
                serverConnectCount.put(serverName,count);
            }else{
                //对应的ServerName的JMX连接获取失败，返回该服务JMX连接失败，用于上报不可用记录
                JMXConnectionInfo jmxConnectionInfo = new JMXConnectionInfo();
                jmxConnectionInfo.setValid(false, JMXUnavailabilityType.connectionFailed);
                connections.add(jmxConnectionInfo);
                serverConnectCount.put(serverName,1);
            }
        }

        //若当前应有的服务实例记录值比获取到的记录值小，重新设置
        if(getServerConnectCount(serverName) < vmDescList.size()){
            serverConnectCount.put(serverName,vmDescList.size());
        }

        return connections;
    }

    private JMXConnector getJMXConnector(JMXConnectUrlInfo jmxConnectUrlInfo) throws Exception {
        JMXServiceURL url = new JMXServiceURL(jmxConnectUrlInfo.getRemoteUrl());
        JMXConnector connector;
        if(jmxConnectUrlInfo.isAuthentication()){
            connector = JMXConnectWithTimeout.connectWithTimeout(url,jmxConnectUrlInfo.getJmxUser()
                    ,jmxConnectUrlInfo.getJmxPassword(),10, TimeUnit.SECONDS);
        }else{
            connector = JMXConnectWithTimeout.connectWithTimeout(url,null,null,10, TimeUnit.SECONDS);
        }
        return connector;
    }

    /**
     * 清楚连接缓存
     */
    private void clearCache(){
        //清除当前连接池中的连接
        List<String> removeKey = connectCacheLibrary.keySet().stream().filter(key -> NumberUtils.isNumber(key.replace(serverName,""))).collect(Collectors.toList());
        removeKey.forEach(key -> {
            connectCacheLibrary.get(key).closeJMXConnector();
            connectCacheLibrary.remove(key);
        });
    }

    /**
     * 重置jmx连接
     * @throws IOException
     */
    public synchronized void resetMBeanConnection() {
        if(StringUtils.isEmpty(serverName)){
            log.error("获取JMX连接的serverName不能为空");
        }

        //本地JMX连接中根据指定的服务名命中的VirtualMachineDescriptor
        List<VirtualMachineDescriptor> targetDesc = getVmDescByServerName(serverName);

        //若命中的target数量大于或等于该服务要求的JMX连接数,则进行重置连接池中的连接
        if(targetDesc.size() >= getServerConnectCount(serverName)){

            //清除当前连接池中的连接
            clearCache();

            //重新设置服务应有连接数
            int count = 0;
            //重新构建连接
            for (VirtualMachineDescriptor desc : targetDesc) {

                JMXConnectUrlInfo jmxConnectUrlInfo = getConnectorAddress(desc);
                if (jmxConnectUrlInfo == null) {
                    log.error("应用{}的JMX连接URL获取失败",serverName);
                    //对应的ServerName的JMX连接获取失败，返回该服务JMX连接失败，用于上报不可用记录
                    initBadJMXConnect(desc);
                    count++;
                    continue;
                }
                try {
                    initJMXConnectionInfo(getJMXConnector(jmxConnectUrlInfo),desc);
                    log.info("应用 {} JMX 连接已建立,将在下一周期获取Metrics值时生效",serverName);
                } catch (Exception e) {
                    log.error("JMX 连接获取异常:{}",e);
                    //JMX连接获取失败，添加该服务JMX的不可用记录，用于上报不可用记录
                    initBadJMXConnect(desc);
                }
                count++;
            }
            serverConnectCount.put(serverName,count);
        }

        //若当前应有的服务实例记录值比获取到的记录值小，重新设置
        if(getServerConnectCount(serverName) < targetDesc.size()){
            serverConnectCount.put(serverName,targetDesc.size());
        }

    }



    private JMXConnectUrlInfo getConnectorAddress(VirtualMachineDescriptor desc){
        if(AgentConfiguration.INSTANCE.isAgentJMXLocalConnect()){
            String connectorAddress = AbstractJmxCommand.findJMXLocalUrlByProcessId(Integer.parseInt(desc.id()));
            if(connectorAddress != null){
                return new JMXConnectUrlInfo(connectorAddress);
            }
        }

        JMXConnectUrlInfo jmxConnectUrlInfo = null;
        try {
            jmxConnectUrlInfo = AbstractJmxCommand.findJMXRemoteUrlByProcessId(Integer.parseInt(desc.id()), InetAddress.getLocalHost().getHostAddress());
            if(jmxConnectUrlInfo != null){
                log.info("JMX Remote URL:{}",jmxConnectUrlInfo);
            }else if(!AgentConfiguration.INSTANCE.isAgentJMXLocalConnect()){
                log.warn("应用未配置JMX Remote功能,请给应用配置JMX Remote");
            }
        } catch (UnknownHostException e) {
            log.error("JMX连接本机地址获取失败",e);
        }
        return jmxConnectUrlInfo;
    }

    /**
     * JMXConnectionInfo的初始化动作
     * @param connector
     * @param desc
     * @return
     * @throws IOException
     */
    private JMXConnectionInfo initJMXConnectionInfo(JMXConnector connector,VirtualMachineDescriptor desc) throws IOException {
        JMXConnectionInfo jmxConnectionInfo = new JMXConnectionInfo();
        jmxConnectionInfo.setJmxConnector(connector);
        jmxConnectionInfo.setCacheKeyId(desc.id());
        jmxConnectionInfo.setConnectionServerName(serverName);
        jmxConnectionInfo.setConnectionQualifiedServerName(desc.displayName());
        jmxConnectionInfo.setMBeanServerConnection(connector.getMBeanServerConnection());
        jmxConnectionInfo.setValid(true,null);
        jmxConnectionInfo.setPid(Integer.parseInt(desc.id()));

        connectCacheLibrary.put(serverName + desc.id(),jmxConnectionInfo);
        return jmxConnectionInfo;
    }

    /**
     * 连接失败的JMX的初始化动作
     * @param desc
     */
    private JMXConnectionInfo initBadJMXConnect(VirtualMachineDescriptor desc){
        JMXConnectionInfo jmxConnectionInfo = new JMXConnectionInfo();
        jmxConnectionInfo.setValid(false,JMXUnavailabilityType.connectionFailed);
        jmxConnectionInfo.setConnectionServerName(serverName);
        jmxConnectionInfo.setConnectionQualifiedServerName(desc.displayName());
        jmxConnectionInfo.setPid(Integer.parseInt(desc.id()));
        connectCacheLibrary.put(serverName + desc.id(),jmxConnectionInfo);
        return jmxConnectionInfo;
    }

} public class JMXConnectionInfo {
    @Getter
    @Setter
    private String cacheKeyId;//JMX缓存的key的唯一id

    @Setter
    private MBeanServerConnection mBeanServerConnection;

    @Getter
    @Setter
    private JMXConnector jmxConnector;

    @Getter
    @Setter
    private String connectionQualifiedServerName;//JMX中的jmx连接限定名

    @Getter
    @Setter
    private String connectionServerName;//配置中指定的服务名

    @Getter
    @Setter
    private String name;//此jmx连接对监控展示的名称

    @Getter
    @Setter
    private int pid;//jmx的进程号

    private boolean valid;

    /**
     * 不可用类型
     * 注：当type为{@link JMXUnavailabilityType#connectionFailed}时，该对象中的{@link JMXConnectionInfo#jmxConnector}才是真正的不可用，其他情况，应该正常进行采集操作
     */
    @Getter
    private JMXUnavailabilityType type;

    /**
     * 关闭JMX连接
     */
    public void closeJMXConnector(){
        ExecuteThreadUtil.execute(() -> {
            if(jmxConnector != null){
                try {
                    jmxConnector.close();
                } catch (Exception ignored) {
                }
            }
        });
    }

    public boolean isValid() {
        return valid;
    }

    /**
     * 设置不可用时自动关闭JMX连接
     * @param valid
     */
    public void setValid(boolean valid,JMXUnavailabilityType type) {
        this.valid = valid;
        if(!valid){
            this.type = type;
            closeJMXConnector();
        }
    }

    @Override
    public String toString() {
        return "JMXConnectionInfo{" +
                "cacheKeyId='" + cacheKeyId + '\'' +
                ", mBeanServerConnection=" + mBeanServerConnection +
                ", jmxConnector=" + jmxConnector +
                ", connectionQualifiedServerName='" + connectionQualifiedServerName + '\'' +
                ", connectionServerName='" + connectionServerName + '\'' +
                ", name='" + name + '\'' +
                ", pid=" + pid +
                ", valid=" + valid +
                ", JMXUnavailabilityType=" + type +
                '}';
    }

    /**
     * 注：请勿使用此对象进行JMX连接交互操作，可能会在JMX维护时发生java.io.IOException: The client has been closed异常 （{@link JMXManager#getJmxMetricValue(String, JMXPlugin)}} 方法除外）
     * @return
     */
    public MBeanServerConnection getMBeanServerConnection() {
        return mBeanServerConnection;
    }
} @Getter
@Setter
@ToString
public class JMXMetricsValueInfo {

    private long timestamp;
    /**
     * 需要监控的配置项
     */
    Set<JMXMetricsConfiguration> jmxMetricsConfigurations;

    /**
     * 此jmx 连接的对象信息
     */
    private List<JMXObjectNameInfo> jmxObjectNameInfoList;

    /**
     * jmx 连接信息
     */
    private JMXConnectionInfo jmxConnectionInfo;

} @Data
public class JMXObjectNameInfo {
    /**
     * 此jmx 连接下的 objectName 的所有监控值
     */
    private Map<String,Object> metricsValue;
    /**
     * JMX ObjectName
     */
    private ObjectName objectName;
    /**
     * 监控连接信息
     */
    private JMXConnectionInfo jmxConnectionInfo;
} public interface JMXPlugin extends Plugin{

    /**
     * 自定义的监控属性的监控值基础配置名
     * @return
     * 若无配置文件,可返回null
     */
    String basePropertiesKey();

    /**
     * 该插件所要监控的服务在JMX连接中的displayName识别名
     * 若有该插件监控的相同类型服务,但是displayName不一样,可用逗号(,)进行分隔,进行统一监控
     * @return
     */
    String jmxServerName();

    /**
     * 该插件监控的服务标记名称,目的是为能够在操作系统中准确定位该插件监控的是哪个具体服务
     * 可用变量:
     * {jmxServerName} - 代表直接使用当前服务的jmxServerName
     * 如该服务运行的端口号等
     * 若不需要指定则可返回null
     * @param jmxMetricsValueInfo
     * 该服务连接的jmx对象信息
     * @param pid
     * 该服务当前运行的进程id
     * @return
     */
    String agentSignName(JMXMetricsValueInfo jmxMetricsValueInfo, int pid);

    /**
     * 插件监控的服务正常运行时的內建监控报告
     * 若有些特殊的监控值无法用配置文件进行配置监控,可利用此方法进行硬编码形式进行获取
     * 注:此方法只有在监控对象可用时,才会调用,并加入到监控值报告中,一并上传
     * @param metricsValueInfo
     * 当前的JMXMetricsValueInfo信息
     * @return
     */
    Collection<FalconReportObject> inbuiltReportObjectsForValid(JMXMetricsValueInfo metricsValueInfo);

    /**
     * 能够代表该JMX服务的绝对路径
     * 若实现此方法,则若该JMX连接不可用时,将会检查该JMX服务的目录是否存在,若不存在,将会清除此连接,并不再监控此JMX。
     * 否则,若JMX连接不可用,将会上报不可用的报告,且不会清除
     * @param pid
     * 服务的进程id
     * @param serverName
     * jmx 服务名
     * @return
     */
    default String serverPath(int pid,String serverName){
        return null;
    }

    /**
     * JMX服务器的目录名称
     * @param pid
     * 服务的进程id
     * @return
     * 默认返回null,既不打目录名称的tag
     */
    default String serverDirName(int pid){
        return null;
    }

} public class CacheUtil {

    /**
     * 获取缓存中，超时的key
     * @param map
     * @return
     */
    public static List<String> getTimeoutCacheKeys(ConcurrentHashMap<String,String> map){
        List<String> keys = new ArrayList<>();
        long now = System.currentTimeMillis();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            long cacheTime = getCacheTime(entry.getValue());
            if(cacheTime == 0){
                keys.add(entry.getKey());
            }else if(now - cacheTime >= 2 * 24 * 60 * 60 * 1000){
                //超时2天
                keys.add(entry.getKey());
            }
        }
        return keys;
    }

    /**
     * 设置缓存值，添加时间戳
     * @param value
     * @return
     */
    public static String setCacheValue(String value){
        if(!StringUtils.isEmpty(value)){
            return String.format("@%d@%s",System.currentTimeMillis(),value);
        }
        return value;
    }

    /**
     * 获取缓存值，去除时间戳
     * @param value
     * @return
     */
    public static String getCacheValue(String value){
        if(!StringUtils.isEmpty(value)){
            return value.replaceAll("@\\d*@","");
        }
        return value;
    }

    /**
     * 获取缓存值中的时间戳
     * @param value
     * @return
     */
    private static long getCacheTime(String value){
        try {
            if(value != null){
                return Long.parseLong(value.replace(getCacheValue(value),"").replace("@",""));
            }
        } catch (Exception e) {
            return 0;
        }
        return 0;
    }

    /**
     * 从行默认tag缓存中获取tag
     * @param addresses
     * @param tagsCache
     * @param address
     * @return
     */
    public static Map<String,String> getTags(Map<String,String> addresses,Map<String,String> tagsCache,String address){
        Map<String,String> map = new HashMap<>();
        String adds;
        String tags;
        if(address.endsWith("[]")){
            // ip[] 形式的，设置 空串 tag
            tags = "";
            adds = address.replace("[]","");
        }else if (address.contains("[") && address.contains("]")){
            // ip[tag=tagValue] 形式的，设置它单独的tag
            adds = address.substring(0,address.indexOf("["));
            tags = address.substring(address.indexOf("[") + 1,address.lastIndexOf("]"));
            tags = tags.replace(";",",");
        }else{
            // ip 形式的，获取 行默认tag
            tags = CacheUtil.getTagFromTagsCacheByAddress(addresses,tagsCache,address);
            adds = address;
        }
        map.put("adds",adds);
        map.put("tags",tags);
        return map;
    }

    private static String getTagFromTagsCacheByAddress(Map<String, String> addresses, Map<String, String> tagsCache, String address){
        if(!StringUtils.isEmpty(address)){
            Optional<String> cacheKey =addresses.keySet().stream()
                    .filter(key -> addresses.get(key) != null && addresses.get(key).contains(address)).findFirst();
            if(cacheKey.isPresent()){
                return tagsCache.get(cacheKey.get());
            }
        }
        return null;
    }

    /**
     * 初始化行默认tag缓存
     * @param addresses
     * @param tagsCache
     */
    public static void initTagsCache(Map<String,String> addresses,Map<String,String> tagsCache){
        addresses.keySet().forEach(key -> {
            String value = addresses.get(key);
            String firstAddress = value.split(",")[0];
            if (!firstAddress.endsWith("[]") && firstAddress.contains("[") && firstAddress.contains("]")) {
                //添加 行默认 的 tag cache
                String tags = firstAddress.substring(firstAddress.indexOf("[") + 1, firstAddress.lastIndexOf("]"));
                tags = tags.replace(";", ",");
                tagsCache.put(key, tags);
            }
        });
    }
} public class MapUtil {

    /**
     * 获取指定Map的拥有相同Value的key集合
     * @param map
     * @param value
     * @return
     */
    public static List<Object> getSameValueKeys(Map map,Object value){
        List<Object> keys = new ArrayList<>();
        if(value != null){
            for (Object key : map.keySet()) {
                if(value.equals(map.get(key))){
                    keys.add(key);
                }
            }
        }
        return keys;
    }
} public class Maths {

	/**
	 * 转换数字为可显示格式，避免出现科学计数法形式
	 * 自动保留2位小数
	 * @param num
	 * @return
	 */
	public static String numFormat(Number num){
		return String.format("%.2f",num);
	}

	// 默认除法运算精度
	private static final int DEF_DIV_SCALE = 10;

	// 这个类不能实例化
	private Maths() {}

	/**
	 * 提供精确的加法运算。
	 * 
	 * @param v1
	 *            被加数
	 * @param v2
	 *            加数
	 * @return 两个参数的和
	 */
	public static double add(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.add(b2).doubleValue();
	}

	/**
	 * 提供精确的减法运算。
	 * 
	 * @param v1
	 *            被减数
	 * @param v2
	 *            减数
	 * @return 两个参数的差
	 */
	public static double sub(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.subtract(b2).doubleValue();
	}

	/**
	 * 提供精确的乘法运算。
	 * 
	 * @param v1
	 *            被乘数
	 * @param v2
	 *            乘数
	 * @return 两个参数的积
	 */
	public static double mul(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.multiply(b2).doubleValue();
	}

	/**
	 * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到 小数点以后10位，以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE);
	}

	/**
	 * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 定精度，以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @param scale
	 *            表示表示需要精确到小数点以后几位。
	 * @return 两个参数的商
	 */
	public static double div(double v1, double v2, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException(
					"The scale must be a positive integer or zero");
		}
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 提供精确的小数位四舍五入处理。
	 * 
	 * @param v
	 *            需要四舍五入的数字
	 * @param scale
	 *            小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static double round(double v, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException(
					"The scale must be a positive integer or zero");
		}
		BigDecimal b = new BigDecimal(Double.toString(v));
		BigDecimal one = new BigDecimal("1");
		return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}
} public class StringUtils {

    /**
     * 判断传入的String是否为空('' or null)
     * @param str
     * @return
     */
    public static boolean isEmpty(String str){
        return str == null || str.length() == 0;
    }

    /**
     * 返回指定数字的字符串形式
     * @param value
     * @return
     */
    public static String getStringByInt(int value){
        return String.valueOf(value).intern();
    }

} @Getter
@Setter
@ToString
public class JMXMetricsConfiguration {

    private String objectName;
    private String metrics;
    private String valueExpress;
    private String alias;
    private String counterType;
    private String tag;
    private boolean hasCollect = false;

}
import com.yiji.falcon.agent.exception.JMXUnavailabilityType;
import com.yiji.falcon.agent.falcon.CounterType;
import com.yiji.falcon.agent.falcon.FalconReportObject;
import com.yiji.falcon.agent.falcon.MetricsType;
import com.yiji.falcon.agent.jmx.JMXConnection;
import com.yiji.falcon.agent.jmx.vo.JMXConnectionInfo;
import com.yiji.falcon.agent.jmx.vo.JMXMetricsValueInfo;
import com.yiji.falcon.agent.jmx.vo.JMXObjectNameInfo;
import com.yiji.falcon.agent.plugins.JMXPlugin;
import com.yiji.falcon.agent.plugins.util.CacheUtil;
import com.yiji.falcon.agent.util.MapUtil;
import com.yiji.falcon.agent.util.Maths;
import com.yiji.falcon.agent.util.StringUtils;
import com.yiji.falcon.agent.vo.jmx.JMXMetricsConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.NumberUtils;
import javax.management.openmbean.CompositeDataSupport;
import java.io.File;
import java.io.IOException;
import java.lang.management.MemoryUsage;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/*
 * www.yiji.com Inc.
 * Copyright (c) 2016 All Rights Reserved
 */
package com.yiji.falcon.agent.plugins.metrics;



/*
 * 修订记录:
 * guqiu@yiji.com 2016-06-22 17:48 创建
 */

/**
 * JMX监控值
 *
 * @author guqiu@yiji.com
 */
@Slf4j
public class JMXMetricsValue extends MetricsCommon {

    private JMXPlugin jmxPlugin;
    private List<JMXMetricsValueInfo> jmxMetricsValueInfos;