Mercurial > hg > heapstats
view analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/jvmlive/jdp/JdpDecoder.java @ 272:dd85c1cbc8c8
Bug 3752: Migrate to OpenJFX 13
Reviewed-by: ykubota
https://github.com/HeapStats/heapstats/pull/144
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Fri, 27 Sep 2019 14:47:03 +0900 |
parents | |
children |
line wrap: on
line source
/* * Copyright (C) 2014-2019 Yasumasa Suenaga * * 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. */ package jp.co.ntt.oss.heapstats.fx.plugin.builtin.jvmlive.jdp; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutorService; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.Labeled; import javafx.scene.control.ListView; import jp.co.ntt.oss.heapstats.fx.lambda.EventHandlerWrapper; import jp.co.ntt.oss.heapstats.jmx.JMXHelper; import jp.co.ntt.oss.heapstats.lambda.RunnableWrapper; import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; /** * JDP packet decoder. * This class containes JDP packet data and method for parse it. * * @author Yasumasa Suenaga */ public class JdpDecoder extends Task<Void>{ /** Key of JDP for main class. */ public static final String MAIN_CLASS_KEY = "MAIN_CLASS"; /** Key of JDP for instance name. */ public static final String INSTANCE_NAME_KEY = "INSTANCE_NAME"; /** Key of JDP for JMX URL. */ public static final String JMX_SERVICE_URL_KEY = "JMX_SERVICE_URL"; /** Key of JDP for UUID. */ public static final String DISCOVERABLE_SESSION_UUID_KEY = "DISCOVERABLE_SESSION_UUID"; /** Key of JDP for broadcast interval. */ public static final String PACKET_BROADCAST_INTERVAL_KEY = "BROADCAST_INTERVAL"; /** Key of JDP for PID. */ public static final String PROCESS_ID_KEY = "PROCESS_ID"; /** Raw JDP packet data. */ private final ByteBuffer jdpRawData; /** * Received time of this instance. */ private LocalDateTime receivedTime; /** Source address of this JDP packet. */ private final InetSocketAddress sourceAddr; /** Main class of this JDP packet. */ private String mainClass; /** Instance name of this JDP packet. */ private String instanceName; /** JMX URL of this JDP packet. */ private String jmxServiceURL; /** PID of this JDP packet. */ private int pid; /** UUID of this JDP packet. */ private UUID uuid; /** Broadcast interval of this JDP packet. */ private int broadcastInterval; /** If this packet is expired, this flag is set to true by JdpValidatorService. */ private final BooleanProperty invalidate; private final ObjectProperty<ObservableList<JdpTableKeyValue>> jdpTableKeyValue; private final ListView<JdpDecoder> jdpList; private final Optional<String> jconsolePath; private final ExecutorService jmxProcPool; private JdpTableKeyValue heapstatsValue; /** * Constructor of JdpDecorder. * * @param sourceAddr Source address of JDP packet. * @param rawData JDP raw data. * @param jdpList ListView which includes JDP. * @param jconsolePath Path to JConsole. * @param jmxProcPool ThreadPool which processes JMX access. */ public JdpDecoder(InetSocketAddress sourceAddr, ByteBuffer rawData, ListView<JdpDecoder> jdpList, Optional<String> jconsolePath, ExecutorService jmxProcPool){ this.receivedTime = LocalDateTime.now(); this.sourceAddr = sourceAddr; this.jdpRawData = rawData; this.jdpList = jdpList; this.invalidate = new SimpleBooleanProperty(false); this.jdpTableKeyValue = new SimpleObjectProperty<>(); this.jconsolePath = jconsolePath; this.jmxProcPool = jmxProcPool; } /** * Create String instance from ByteBuffer. * * @param buffer ByteBuffer to parse. Position must be set to top of String data. * @return String from ByteBuffer. */ private String getStringFromByteBuffer(ByteBuffer buffer){ int length = Short.toUnsignedInt(buffer.getShort()); byte[] rawValue = new byte[length]; buffer.get(rawValue); return new String(rawValue, StandardCharsets.UTF_8); } private Map<String, String> parseJdpPacket(){ if(jdpRawData.getInt() != 0xC0FFEE42){ throw new RuntimeException("Invalid magic number."); } if(jdpRawData.getShort() != 1){ throw new RuntimeException("Invalid JDP version."); } Map<String, String> jdpContents = new HashMap<>(); while(jdpRawData.hasRemaining()){ String key = getStringFromByteBuffer(jdpRawData); String value = getStringFromByteBuffer(jdpRawData); jdpContents.put(key, value); } return jdpContents; } private void setJdpTableKeyValue(JdpTableKeyValue val, Labeled jmxURL){ switch(val.keyProperty().get()){ case "Received Time": val.valueProperty().set(receivedTime.format(HeapStatsUtils.getDateTimeFormatter())); break; case "Address": val.valueProperty().set(sourceAddr.getAddress().getHostAddress()); break; case "JDP Instance Name": val.valueProperty().set(instanceName); break; case "Main Class": val.valueProperty().set(mainClass); break; case "UUID": val.valueProperty().set(uuid.toString()); break; case "PID": val.valueProperty().set(Integer.toString(pid)); break; case "JMX URL": val.valueProperty().set(jmxURL); } } /** * Update JDP packet if same UUID is registered in JDP list. */ private void updateJDPData(){ Labeled jmxURL; if(jconsolePath.isPresent()){ String[] execParam = {jconsolePath.get(), jmxServiceURL}; jmxURL = new Hyperlink(jmxServiceURL); ((Hyperlink)jmxURL).setOnAction(new EventHandlerWrapper<>(e -> Runtime.getRuntime().exec(execParam))); } else{ jmxURL = new Label(jmxServiceURL); } int idx = jdpList.getItems().indexOf(this); if(idx == -1){ heapstatsValue = new JdpTableKeyValue("HeapStats", "Checking..."); jmxProcPool.submit(new RunnableWrapper(() -> heapstatsValue.valueProperty().set(new JMXHelper(jmxServiceURL)))); jdpTableKeyValue.set(FXCollections.observableArrayList( new JdpTableKeyValue("Received Time", receivedTime.format(HeapStatsUtils.getDateTimeFormatter())), new JdpTableKeyValue("Address", sourceAddr.getAddress().getHostAddress()), new JdpTableKeyValue("JDP Instance Name", instanceName), new JdpTableKeyValue("Main Class", mainClass), new JdpTableKeyValue("UUID", uuid.toString()), new JdpTableKeyValue("PID", Integer.toString(pid)), new JdpTableKeyValue("JMX URL", jmxURL), heapstatsValue )); jdpList.getItems().add(this); } else{ JdpDecoder existData = jdpList.getItems().get(idx); existData.receivedTime = receivedTime; existData.jdpTableKeyValue.get().forEach(p -> setJdpTableKeyValue(p, jmxURL)); } } @Override protected Void call() throws Exception { Map<String, String> jdpData = parseJdpPacket(); this.mainClass = jdpData.get(MAIN_CLASS_KEY); this.instanceName = jdpData.get(INSTANCE_NAME_KEY); this.jmxServiceURL = jdpData.get(JMX_SERVICE_URL_KEY); this.uuid = UUID.fromString(jdpData.get(DISCOVERABLE_SESSION_UUID_KEY)); this.pid = Integer.parseInt(jdpData.get(PROCESS_ID_KEY)); this.broadcastInterval = Integer.parseInt(jdpData.get(PACKET_BROADCAST_INTERVAL_KEY)); Platform.runLater(this::updateJDPData); return null; } /** * Get time of this JDP packet was received. * * @return Received time. */ public LocalDateTime getReceivedTime() { return receivedTime; } /** * Get source address of JDP packet. * * @return Source address of JDP. */ public InetSocketAddress getSourceAddr() { return sourceAddr; } /** * Get main class in JDP packet. * * @return Main class. */ public String getMainClass() { return mainClass; } /** * Get JMX URL in JDP packet. * * @return JDP URL. */ public String getJmxServiceURL() { return jmxServiceURL; } /** * Get PID in JDP packet. * @return PID */ public int getPid() { return pid; } /** * Get UUID in JDP packet. * @return UUID */ public UUID getUuid() { return uuid; } /** * Get instance name in JDP packet. * @return Instance name. */ public String getInstanceName() { return instanceName; } /** * Get broadcast interval in JDP packet. * * @return JDP broadcast interval. */ public int getBroadcastInterval() { return broadcastInterval; } /** * Set invalidate to this JDP packet. */ public void setInvalidate(){ invalidate.set(true); } /** * Get invalidate property. * * @return Invalidate property. */ public BooleanProperty invalidateProperty(){ return this.invalidate; } /** * Get JDP packet data list. * This method returns key-value list. * * @return JDP packet data list. */ public ObjectProperty<ObservableList<JdpTableKeyValue>> jdpTableKeyValueProperty(){ return this.jdpTableKeyValue; } /** * Get JDP Key-Value. * * @return JDP packet data. */ public JdpTableKeyValue getHeapStatsTableKeyValue(){ return this.heapstatsValue; } @Override public int hashCode() { return this.uuid.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } return this.uuid.equals(((JdpDecoder)obj).uuid); } @Override public String toString() { return Optional.ofNullable(instanceName).orElse(mainClass); } }