Mercurial > hg > heapstats
view analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveController.java @ 243:be9892e921b3
Bug 3420: Migrate Plugin API module from fx module
Reviewed-by: yasuenag
GitHub: https://github.com/HeapStats/heapstats/pull/109
author | KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp> |
---|---|
date | Mon, 17 Jul 2017 23:44:04 +0900 |
parents | 0b14cdefe0b8 |
children |
line wrap: on
line source
/* * Copyright (C) 2014-2017 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.plugin.builtin.jvmlive; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.rmi.ConnectException; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import javafx.beans.binding.Bindings; import javafx.concurrent.ScheduledService; import javafx.event.ActionEvent; import javafx.event.Event; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Hyperlink; import javafx.scene.control.Labeled; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.MenuItem; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextArea; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.stage.FileChooser; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.util.Duration; import jp.co.ntt.oss.heapstats.MainWindowController; import jp.co.ntt.oss.heapstats.jmx.JMXHelper; import jp.co.ntt.oss.heapstats.lambda.ConsumerWrapper; import jp.co.ntt.oss.heapstats.plugin.PluginController; import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.errorreporter.ErrorReportDecoder; import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.errorreporter.ErrorReportServer; import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpDecoder; import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpReceiver; import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpTableKeyValue; import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.jdp.JdpValidatorService; import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.mbean.HeapStatsMBeanController; import jp.co.ntt.oss.heapstats.utils.HeapStatsConfigException; import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; /** * FXML Controller class for JVMLive * * @author Yasumasa Suenaga */ public class JVMLiveController extends PluginController implements Initializable { @FXML private ListView<JdpDecoder> jvmList; @FXML private TableView<JdpTableKeyValue> detailTable; @FXML private TableColumn<JdpTableKeyValue, String> jdpKey; @FXML private TableColumn<JdpTableKeyValue, Object> jdpValue; @FXML private ListView<ErrorReportDecoder> crashList; @FXML private MenuItem detailsMenu; @FXML private MenuItem saveMenu; private JdpReceiver jdpReceiver; private Thread receiverThread; private ScheduledService<Void> jdpValidator; private ExecutorService taskThreadPool; private ExecutorService jmxThreadPool; private ErrorReportServer errorReportServer; private Thread errorReportThread; private Optional<String> jconsolePath; /** * Initializes the controller class. */ @Override public void initialize(URL url, ResourceBundle rb) { try { JVMLiveConfig.load(); } catch (IOException | HeapStatsConfigException ex) { HeapStatsUtils.showExceptionDialog(ex); } jdpKey.setCellValueFactory(new PropertyValueFactory<>("key")); jdpValue.setCellValueFactory(new PropertyValueFactory<>("value")); jdpValue.setCellFactory(c -> new TableCell<JdpTableKeyValue, Object>(){ @Override protected void updateItem(Object item, boolean empty) { super.updateItem(item, empty); Optional.ofNullable(item).ifPresent(i -> { try{ if(i instanceof Labeled){ setGraphic((Labeled)i); } else if(i instanceof JMXHelper){ setGraphic(createHyperlink((JMXHelper)i)); } else{ setText((String)i); } } catch(Throwable t){ setText("<N/A>"); } }); } } ); jvmList.getSelectionModel().selectedItemProperty() .addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(d -> showJdpDecoderDetail((JdpDecoder)d))); jvmList.setCellFactory(l -> new ListCell<JdpDecoder>(){ @Override protected void updateItem(JdpDecoder jdp, boolean b) { super.updateItem(jdp, b); Optional.ofNullable(jdp) .ifPresent(d -> { setText(d.toString()); styleProperty().bind(Bindings.createStringBinding(() -> d.invalidateProperty().get() ? "-fx-text-fill: orange;" : "-fx-text-fill: black;", d.invalidateProperty())); }); } } ); Path jdkBase = Paths.get(Paths.get(System.getProperties().getProperty("java.home")).getParent().toString(), "bin"); File f = new File(jdkBase.toString(), "jconsole"); if(f.canExecute()){ jconsolePath = Optional.of(f.getAbsolutePath()); } else{ f = new File(jdkBase.toString(), "jconsole.exe"); if(f.canExecute()){ jconsolePath = Optional.of(f.getAbsolutePath()); } else{ jconsolePath = Optional.empty(); } } taskThreadPool = Executors.newCachedThreadPool(); jmxThreadPool = Executors.newCachedThreadPool(); try { jdpReceiver = new JdpReceiver(jvmList, taskThreadPool, jconsolePath, jmxThreadPool); receiverThread = new Thread(jdpReceiver, "JDP receiver thread"); receiverThread.start(); jdpValidator = new JdpValidatorService(jvmList); jdpValidator.setPeriod(Duration.seconds(2)); jdpValidator.start(); } catch (UnknownHostException ex) { HeapStatsUtils.showExceptionDialog(ex); } errorReportServer = new ErrorReportServer(JVMLiveConfig.getErrorReportServerPort(), crashList.getItems(), taskThreadPool); errorReportThread = new Thread(errorReportServer, "ErrorReport receiver thread"); errorReportThread.start(); detailsMenu.disableProperty().bind(crashList.selectionModelProperty().getValue().selectedItemProperty().isNull()); saveMenu.disableProperty().bind(crashList.selectionModelProperty().getValue().selectedItemProperty().isNull()); } private void onHeapStatsLinkClicked(JMXHelper jmxHelper){ ResourceBundle pluginResource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); FXMLLoader loader = new FXMLLoader(JVMLiveController.class.getResource("/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/heapstatsMBean.fxml"), pluginResource); HeapStatsMBeanController mbeanController; Scene mbeanDialogScene; try { loader.load(); mbeanController = (HeapStatsMBeanController)loader.getController(); mbeanDialogScene = new Scene(loader.getRoot()); } catch (IOException ex) { HeapStatsUtils.showExceptionDialog(ex); return; } mbeanController.setJmxHelper(jmxHelper); mbeanController.loadAllConfigs(); Stage dialog = new Stage(StageStyle.UTILITY); try(InputStream icon = MainWindowController.class.getResourceAsStream("heapstats-icon.png")){ dialog.getIcons().add(new Image(icon)); } catch(IOException e){ HeapStatsUtils.showExceptionDialog(e); } dialog.setScene(mbeanDialogScene); dialog.initModality(Modality.APPLICATION_MODAL); dialog.setResizable(false); dialog.setTitle("HeapStats configuration @ " + jmxHelper.getUrl().toString()); dialog.showAndWait(); } private Hyperlink createHyperlink(JMXHelper jmxHelper){ Hyperlink heapstatsLink = new Hyperlink(jmxHelper.getMbean().getHeapStatsVersion()); heapstatsLink.setOnAction(e -> onHeapStatsLinkClicked(jmxHelper)); return heapstatsLink; } private void showHsErrDialog(ErrorReportDecoder data){ String host = data.toString(); String hsErr; try(BufferedReader reader = Files.newBufferedReader(data.getHsErrFile().toPath())){ hsErr = reader.lines().collect(Collectors.joining("\n")); } catch (IOException ex) { HeapStatsUtils.showExceptionDialog(ex); return; } Alert dialog = new Alert(AlertType.WARNING); ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); dialog.setTitle(resource.getString("dialog.crashreport.title")); dialog.setHeaderText(resource.getString("dialog.crashreport.header") + host); TextArea hsErrArea = new TextArea(hsErr); hsErrArea.setEditable(false); dialog.getDialogPane().setExpandableContent(hsErrArea); dialog.showAndWait(); } @FXML private void onCrashHistoryClicked(MouseEvent event){ if(event.getButton().equals(MouseButton.PRIMARY) && (event.getClickCount() == 2)){ showHsErrDialog(crashList.getSelectionModel().getSelectedItem()); } } @FXML private void onDetailsMenuClicked(ActionEvent event){ showHsErrDialog(crashList.getSelectionModel().getSelectedItem()); } @FXML private void onSaveMenuClicked(ActionEvent event){ FileChooser dialog = new FileChooser(); ResourceBundle resource = ResourceBundle.getBundle("jvmliveResources", new Locale(HeapStatsUtils.getLanguage())); dialog.setTitle(resource.getString("dialog.crashreport.save.title")); dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Log file (*.log)", "*.log"), new FileChooser.ExtensionFilter("All files", "*.*")); Path hs_err_path = crashList.getSelectionModel().getSelectedItem().getHsErrFile().toPath(); Optional.ofNullable(dialog.showSaveDialog(MainWindowController.getInstance().getOwner())) .ifPresent(new ConsumerWrapper<>(t -> Files.copy(hs_err_path, t.toPath(), StandardCopyOption.REPLACE_EXISTING))); } private void showJdpDecoderDetail(JdpDecoder data){ detailTable.setItems(data.jdpTableKeyValueProperty().get()); } @Override public String getPluginName() { return "JVMLive"; } @Override public String getLicense() { return PluginController.LICENSE_GPL_V2; } @Override public Map<String, String> getLibraryLicense() { return null; } @Override public EventHandler<Event> getOnPluginTabSelected() { // do nothing return null; } private void closeJMXConnection(Object obj){ if(obj instanceof JMXHelper){ try{ ((JMXHelper)obj).close(); } catch(ConnectException ex){ // Do nothing // Runtime will connect remote host when JMX connection will be closed. // So we can skip this operation. Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { HeapStatsUtils.showExceptionDialog(ex); } } } @Override public Runnable getOnCloseRequest() { return () -> { Optional.ofNullable(jdpValidator).ifPresent(p -> p.cancel()); Optional.ofNullable(jdpReceiver).ifPresent(r -> r.cancel(true)); Optional.ofNullable(receiverThread).ifPresent(new ConsumerWrapper<>(t -> t.join())); errorReportServer.cancel(true); try{ errorReportThread.join(); } catch (InterruptedException ex){ Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); } taskThreadPool.shutdownNow(); try { taskThreadPool.awaitTermination(JVMLiveConfig.getThreadpoolShutdownAwaitTime(), TimeUnit.SECONDS); } catch (InterruptedException ex) { Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); } jmxThreadPool.shutdownNow(); try { jmxThreadPool.awaitTermination(JVMLiveConfig.getThreadpoolShutdownAwaitTime(), TimeUnit.SECONDS); } catch (InterruptedException ex) { Logger.getLogger(JVMLiveController.class.getName()).log(Level.SEVERE, null, ex); } jvmList.getItems().forEach(p -> closeJMXConnection(p.getHeapStatsTableKeyValue().valueProperty().get())); }; } }