# HG changeset patch # User KUBOTA Yuji # Date 1500302644 -32400 # Node ID be9892e921b3dc3e3172d4ea410fa73ca0f98be3 # Parent f1096afde0362035e9c9c000b6b9ae63707ee707 Bug 3420: Migrate Plugin API module from fx module Reviewed-by: yasuenag GitHub: https://github.com/HeapStats/heapstats/pull/109 diff -r f1096afde036 -r be9892e921b3 .idea/compiler.xml --- a/.idea/compiler.xml Fri Jul 14 12:18:45 2017 +0900 +++ b/.idea/compiler.xml Mon Jul 17 23:44:04 2017 +0900 @@ -9,7 +9,9 @@ + + @@ -17,7 +19,9 @@ + + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 .idea/encodings.xml --- a/.idea/encodings.xml Fri Jul 14 12:18:45 2017 +0900 +++ b/.idea/encodings.xml Mon Jul 17 23:44:04 2017 +0900 @@ -5,6 +5,8 @@ + + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 .idea/modules.xml --- a/.idea/modules.xml Fri Jul 14 12:18:45 2017 +0900 +++ b/.idea/modules.xml Mon Jul 17 23:44:04 2017 +0900 @@ -6,7 +6,9 @@ + + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 .ignore --- a/.ignore Fri Jul 14 12:18:45 2017 +0900 +++ b/.ignore Mon Jul 17 23:44:04 2017 +0900 @@ -25,6 +25,7 @@ agent/attacher/dist/*.jar agent/attacher/heapstats-attacher analyzer/cli/heapstats-cli +analyzer/fx/heapstats-analyzer *.o *.lo *.Po diff -r f1096afde036 -r be9892e921b3 ChangeLog --- a/ChangeLog Fri Jul 14 12:18:45 2017 +0900 +++ b/ChangeLog Mon Jul 17 23:44:04 2017 +0900 @@ -1,3 +1,7 @@ +2017-07-17 KUBOTA Yuji + + * Bug 3420: Migrate Plugin API module from fx module + 2017-07-14 Yasumasa Suenaga * Bug 3421: Override functions might crash on Fedora 26 diff -r f1096afde036 -r be9892e921b3 Makefile.am --- a/Makefile.am Fri Jul 14 12:18:45 2017 +0900 +++ b/Makefile.am Mon Jul 17 23:44:04 2017 +0900 @@ -31,6 +31,7 @@ $(INSTALL_DATA) $(ANALYZER_DIR)/filterDefine.xsd $(DESTDIR)/$(libexecdir) $(INSTALL_DATA) $(ANALYZER_DIR)/heapstats.properties $(DESTDIR)/$(libexecdir) $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-core.jar $(DESTDIR)/$(libexecdir)/lib + $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-plugin-api.jar $(DESTDIR)/$(libexecdir)/lib $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-mbean.jar $(DESTDIR)/$(libexecdir)/lib $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-jmx-helper.jar $(DESTDIR)/$(libexecdir)/lib $(INSTALL_DATA) $(ANALYZER_DIR)/lib/jgraphx.jar $(DESTDIR)/$(libexecdir)/lib diff -r f1096afde036 -r be9892e921b3 Makefile.in --- a/Makefile.in Fri Jul 14 12:18:45 2017 +0900 +++ b/Makefile.in Mon Jul 17 23:44:04 2017 +0900 @@ -810,6 +810,7 @@ $(INSTALL_DATA) $(ANALYZER_DIR)/filterDefine.xsd $(DESTDIR)/$(libexecdir) $(INSTALL_DATA) $(ANALYZER_DIR)/heapstats.properties $(DESTDIR)/$(libexecdir) $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-core.jar $(DESTDIR)/$(libexecdir)/lib + $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-plugin-api.jar $(DESTDIR)/$(libexecdir)/lib $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-mbean.jar $(DESTDIR)/$(libexecdir)/lib $(INSTALL_DATA) $(ANALYZER_DIR)/lib/heapstats-jmx-helper.jar $(DESTDIR)/$(libexecdir)/lib $(INSTALL_DATA) $(ANALYZER_DIR)/lib/jgraphx.jar $(DESTDIR)/$(libexecdir)/lib diff -r f1096afde036 -r be9892e921b3 analyzer/cli/heapstats-cli.iml --- a/analyzer/cli/heapstats-cli.iml Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/cli/heapstats-cli.iml Mon Jul 17 23:44:04 2017 +0900 @@ -10,8 +10,8 @@ - + - + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 analyzer/core/heapstats-core.iml --- a/analyzer/core/heapstats-core.iml Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/core/heapstats-core.iml Mon Jul 17 23:44:04 2017 +0900 @@ -10,4 +10,4 @@ - + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 analyzer/fx/heapstats-analyzer.iml --- a/analyzer/fx/heapstats-analyzer.iml Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/heapstats-analyzer.iml Mon Jul 17 23:44:04 2017 +0900 @@ -11,8 +11,9 @@ - + + @@ -24,4 +25,4 @@ - + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 analyzer/fx/pom.xml --- a/analyzer/fx/pom.xml Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/pom.xml Mon Jul 17 23:44:04 2017 +0900 @@ -82,6 +82,11 @@ ${project.groupId} + heapstats-plugin-api + ${project.version} + + + ${project.groupId} heapstats-core ${project.version} diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/AboutDialogController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/AboutDialogController.java Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/AboutDialogController.java Mon Jul 17 23:44:04 2017 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 Yasumasa Suenaga + * 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 @@ -97,12 +97,12 @@ * Thus I create array of AbstractMap.SimpleEntry . */ List> plugins = new ArrayList<>(); - WindowController.getInstance().getPluginList().forEach((k, v) -> plugins.add(new AbstractMap.SimpleEntry<>(k, v.getLicense()))); + MainWindowController.getInstance().getPluginList().forEach((k, v) -> plugins.add(new AbstractMap.SimpleEntry<>(k, v.getLicense()))); pluginTable.getItems().addAll(plugins); /* Set library license to libraryTable */ List libraryList = new ArrayList<>(); - WindowController.getInstance().getPluginList().forEach((n, c) -> Optional.ofNullable(c.getLibraryLicense()) + MainWindowController.getInstance().getPluginList().forEach((n, c) -> Optional.ofNullable(c.getLibraryLicense()) .ifPresent(l -> l.forEach((k, v) -> libraryList.add(new PluginController.LibraryLicense(n, k, v))))); libraryTable.getItems().addAll(libraryList); } diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/HeapStatsFXAnalyzer.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/HeapStatsFXAnalyzer.java Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/HeapStatsFXAnalyzer.java Mon Jul 17 23:44:04 2017 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 Yasumasa Suenaga + * 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 @@ -37,7 +37,7 @@ */ public class HeapStatsFXAnalyzer extends Application { - private WindowController windowController; + private MainWindowController mainWindowController; @Override public void start(Stage stage) throws Exception { @@ -58,10 +58,11 @@ Parent root = mainWindowLoader.load(); Scene scene = new Scene(root); - windowController = (WindowController) mainWindowLoader.getController(); - windowController.setOwner(stage); - windowController.setHostServices(getHostServices()); - windowController.loadPlugin(); + HeapStatsUtils.setWindowController(mainWindowLoader.getController()); + mainWindowController = (MainWindowController) HeapStatsUtils.getWindowController(); + mainWindowController.setOwner(stage); + mainWindowController.setHostServices(getHostServices()); + mainWindowController.loadPlugin(); stage.setScene(scene); stage.show(); @@ -69,7 +70,7 @@ @Override public void stop() throws Exception { - windowController.getPluginList().values().forEach(c -> Optional.ofNullable(c.getOnCloseRequest()).ifPresent(r -> r.run())); + mainWindowController.getPluginList().values().forEach(c -> Optional.ofNullable(c.getOnCloseRequest()).ifPresent(r -> r.run())); } /** diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/MainWindowController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/MainWindowController.java Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,382 @@ +/* + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javafx.application.HostServices; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TextInputDialog; +import javafx.scene.image.Image; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.Window; +import jp.co.ntt.oss.heapstats.lambda.FunctionWrapper; +import jp.co.ntt.oss.heapstats.plugin.PluginController; +import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.JVMLiveController; +import jp.co.ntt.oss.heapstats.plugin.builtin.log.LogController; +import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotController; +import jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder.ThreadRecorderController; +import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; + +/** + * Main window controller. + * + * @author Yasumasa Suenaga + */ +public class MainWindowController implements Initializable, WindowController { + + private Map pluginList; + + private Region veil; + + private ProgressIndicator progress; + + private ClassLoader pluginClassLoader; + + private Window owner; + + @FXML + private StackPane stackPane; + + @FXML + private TabPane tabPane; + + private AboutDialogController aboutDialogController; + + private Scene aboutDialogScene; + + private static MainWindowController thisController; + + private HostServices hostServices; + + @FXML + private void onExitClick(ActionEvent event) { + Platform.exit(); + } + + @FXML + private void onRankLevelClick(ActionEvent event){ + TextInputDialog dialog = new TextInputDialog(Integer.toString(HeapStatsUtils.getRankLevel())); + + try(InputStream icon = getClass().getResourceAsStream("heapstats-icon.png")){ + Stage dialogStage = (Stage)dialog.getDialogPane().getScene().getWindow(); + dialogStage.getIcons().add(new Image(icon)); + } + catch(IOException e){ + HeapStatsUtils.showExceptionDialog(e); + } + + dialog.setTitle("Rank Level setting"); + dialog.setHeaderText("Rank Level setting"); + ResourceBundle resource = ResourceBundle.getBundle("HeapStatsResources", new Locale(HeapStatsUtils.getLanguage())); + dialog.setContentText(resource.getString("rank.label")); + dialog.showAndWait() + .ifPresent(v -> HeapStatsUtils.setRankLevel(Integer.parseInt(v))); + } + + @FXML + private void onHowToClick(ActionEvent event) { + hostServices.showDocument("http://icedtea.classpath.org/wiki/HeapStats/Analyzer-version2"); + } + + @FXML + private void onAboutMenuClick(ActionEvent event){ + Stage dialog = new Stage(StageStyle.UTILITY); + aboutDialogController.setStage(dialog); + + dialog.setScene(aboutDialogScene); + dialog.initModality(Modality.APPLICATION_MODAL); + dialog.setResizable(false); + dialog.setTitle("About HeapStats Analyzer"); + dialog.showAndWait(); + } + + private void addPlugin(String packageName){ + String lastPackageName = packageName.substring(packageName.lastIndexOf('.') + 1); + packageName = packageName.replace('.', '/'); + String fxmlName = packageName + "/" + lastPackageName + ".fxml"; + FXMLLoader loader; + + try{ + ResourceBundle pluginResource = ResourceBundle.getBundle(lastPackageName + "Resources", new Locale(HeapStatsUtils.getLanguage()), pluginClassLoader); + loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName), pluginResource); + } + catch(MissingResourceException e){ + loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName)); + } + + Parent root; + + try { + root = loader.load(); + } + catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + return; + } + + PluginController controller = (PluginController)loader.getController(); + controller.setVeil(veil); + controller.setProgress(progress); + + Tab tab = new Tab(); + tab.setText(controller.getPluginName()); + tab.setContent(root); + tab.setOnSelectionChanged(controller.getOnPluginTabSelected()); + + tabPane.getTabs().add(tab); + + pluginList.put(controller.getPluginName(), controller); + } + + public static MainWindowController getInstance(){ + return thisController; + } + + @FXML + private void onGCAllClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpGCStatisticsToCSV(false); + } + + @FXML + private void onGCSelectedClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpGCStatisticsToCSV(true); + } + + @FXML + private void onHeapAllClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpClassHistogramToCSV(false); + } + + @FXML + private void onHeapSelectedClick(ActionEvent event) { + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + snapShotController.dumpClassHistogramToCSV(true); + } + + @FXML + private void onSnapShotOpenClick(ActionEvent event) { + Tab snapShotTab = tabPane.getTabs().stream() + .filter(t -> t.getText().equals("SnapShot Data")) + .findAny() + .orElseThrow(() -> new IllegalStateException("SnapShot plugin must be loaded.")); + tabPane.getSelectionModel().select(snapShotTab); + SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); + + snapShotController.onSnapshotFileClick(event); + } + + @FXML + private void onLogOpenClick(ActionEvent event) { + Tab snapShotTab = tabPane.getTabs().stream() + .filter(t -> t.getText().equals("Log Data")) + .findAny() + .orElseThrow(() -> new IllegalStateException("Log plugin must be loaded.")); + tabPane.getSelectionModel().select(snapShotTab); + LogController logController = (LogController)getPluginController("Log Data"); + + logController.onLogFileClick(event); + } + + @FXML + private void onThreadRecorderOpenClick(ActionEvent event) { + Tab threadRecorderTab = tabPane.getTabs().stream() + .filter(t -> t.getText().equals("Thread Recorder")) + .findAny() + .orElseThrow(() -> new IllegalStateException("Thread Recorder plugin must be loaded.")); + tabPane.getSelectionModel().select(threadRecorderTab); + ThreadRecorderController threadRecorderController = (ThreadRecorderController)getPluginController("Thread Recorder"); + + threadRecorderController.onOpenBtnClick(event); + } + + private void initializeAboutDialog(){ + FXMLLoader loader = new FXMLLoader(getClass().getResource("/jp/co/ntt/oss/heapstats/aboutDialog.fxml"), HeapStatsUtils.getResourceBundle()); + + try { + loader.load(); + aboutDialogController = (AboutDialogController)loader.getController(); + aboutDialogScene = new Scene(loader.getRoot()); + } + catch (IOException ex) { + HeapStatsUtils.showExceptionDialog(ex); + } + + } + + @Override + public void initialize(URL url, ResourceBundle rb) { + thisController = this; + pluginList = new ConcurrentHashMap<>(); + veil = new Region(); + veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.2)"); + veil.setVisible(false); + + progress = new ProgressIndicator(); + progress.setMaxSize(200.0d, 200.0d); + progress.setVisible(false); + + stackPane.getChildren().add(veil); + stackPane.getChildren().add(progress); + + initializeAboutDialog(); + } + + /** + * Set HostServices to open HowTo page. + * @param hostServices HostServices + */ + protected void setHostServices(HostServices hostServices) { + this.hostServices = hostServices; + } + + private ClassLoader createPluginClassLoader(String appJarString){ + Path appJarPath; + + try{ + appJarPath = Paths.get(appJarString); + } + catch(InvalidPathException e){ + if((appJarString.charAt(0) == '/') && (appJarString.length() > 2)){ // for Windows + appJarPath = Paths.get(appJarString.substring(1)); + } + else{ + throw e; + } + } + + Path libPath = appJarPath.getParent().resolve("lib"); + URL[] jarURLList = null; + + try(DirectoryStream jarPaths = Files.newDirectoryStream(libPath, "*.jar")){ + jarURLList = StreamSupport.stream(jarPaths.spliterator(), false) + .map(new FunctionWrapper<>(p -> p.toUri().toURL())) + .filter(u -> !u.getFile().endsWith("heapstats-core.jar")) + .filter(u -> !u.getFile().endsWith("heapstats-mbean.jar")) + .filter(u -> !u.getFile().endsWith("jgraphx.jar")) + .collect(Collectors.toList()) + .toArray(new URL[0]); + } + catch(IOException ex) { + Logger.getLogger(MainWindowController.class.getName()).log(Level.SEVERE, null, ex); + } + + return (jarURLList == null) ? MainWindowController.class.getClassLoader() : new URLClassLoader(jarURLList); + } + + /** + * Load plugins which is defined in heapstats.properties. + */ + public void loadPlugin(){ + String resourceName = "/" + this.getClass().getName().replace('.', '/') + ".class"; + String appJarString = this.getClass().getResource(resourceName).getPath(); + + pluginClassLoader = appJarString.contains("!") ? createPluginClassLoader(appJarString.substring(0, appJarString.indexOf('!')).replaceFirst("file:", "")) + : MainWindowController.class.getClassLoader(); + + FXMLLoader.setDefaultClassLoader(pluginClassLoader); + + List plugins = new ArrayList<>(); + /* Add built-in plugins */ + plugins.add(LogController.class.getPackage().getName()); + plugins.add(SnapShotController.class.getPackage().getName()); + plugins.add(ThreadRecorderController.class.getPackage().getName()); + plugins.add(JVMLiveController.class.getPackage().getName()); + /* Add customized plugins by config */ + plugins.addAll(HeapStatsUtils.getPlugins()); + plugins.stream().distinct().forEach(s -> addPlugin(s)); + + aboutDialogController.setPluginInfo(); + } + + /** + * Get controller instance of plugin. + * + * @param pluginName Plugin name which you want. + * @return Controller of Plugin. If it does not exist, return null. + */ + public PluginController getPluginController(String pluginName){ + return pluginList.get(pluginName); + } + + /** + * Get loaded plugin list. + * + * @return Loaded plugin list. + */ + public Map getPluginList() { + return pluginList; + } + + /** + * Select plugin tab + * + * @param pluginName Name of plugin to active. + */ + public void selectTab(String pluginName) throws IllegalArgumentException{ + Tab target = tabPane.getTabs().stream() + .filter(t -> t.getText().equals(pluginName)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(pluginName + " is not loaded.")); + tabPane.getSelectionModel().select(target); + } + + public Window getOwner() { + return owner; + } + + public void setOwner(Window owner) { + this.owner = owner; + } + +} diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/WindowController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/WindowController.java Fri Jul 14 12:18:45 2017 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2014-2015 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; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.MissingResourceException; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import javafx.application.HostServices; -import javafx.application.Platform; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.control.TextInputDialog; -import javafx.scene.image.Image; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import javafx.stage.Modality; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import javafx.stage.Window; -import jp.co.ntt.oss.heapstats.lambda.FunctionWrapper; -import jp.co.ntt.oss.heapstats.plugin.PluginController; -import jp.co.ntt.oss.heapstats.plugin.builtin.log.LogController; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotController; -import jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder.ThreadRecorderController; -import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; - -/** - * Main window controller. - * - * @author Yasumasa Suenaga - */ -public class WindowController implements Initializable { - - private Map pluginList; - - private Region veil; - - private ProgressIndicator progress; - - private ClassLoader pluginClassLoader; - - private Window owner; - - @FXML - private StackPane stackPane; - - @FXML - private TabPane tabPane; - - private AboutDialogController aboutDialogController; - - private Scene aboutDialogScene; - - private static WindowController thisController; - - private HostServices hostServices; - - @FXML - private void onExitClick(ActionEvent event) { - Platform.exit(); - } - - @FXML - private void onRankLevelClick(ActionEvent event){ - TextInputDialog dialog = new TextInputDialog(Integer.toString(HeapStatsUtils.getRankLevel())); - - try(InputStream icon = getClass().getResourceAsStream("heapstats-icon.png")){ - Stage dialogStage = (Stage)dialog.getDialogPane().getScene().getWindow(); - dialogStage.getIcons().add(new Image(icon)); - } - catch(IOException e){ - HeapStatsUtils.showExceptionDialog(e); - } - - dialog.setTitle("Rank Level setting"); - dialog.setHeaderText("Rank Level setting"); - ResourceBundle resource = ResourceBundle.getBundle("HeapStatsResources", new Locale(HeapStatsUtils.getLanguage())); - dialog.setContentText(resource.getString("rank.label")); - dialog.showAndWait() - .ifPresent(v -> HeapStatsUtils.setRankLevel(Integer.parseInt(v))); - } - - @FXML - private void onHowToClick(ActionEvent event) { - hostServices.showDocument("http://icedtea.classpath.org/wiki/HeapStats/Analyzer-version2"); - } - - @FXML - private void onAboutMenuClick(ActionEvent event){ - Stage dialog = new Stage(StageStyle.UTILITY); - aboutDialogController.setStage(dialog); - - dialog.setScene(aboutDialogScene); - dialog.initModality(Modality.APPLICATION_MODAL); - dialog.setResizable(false); - dialog.setTitle("About HeapStats Analyzer"); - dialog.showAndWait(); - } - - private void addPlugin(String packageName){ - String lastPackageName = packageName.substring(packageName.lastIndexOf('.') + 1); - packageName = packageName.replace('.', '/'); - String fxmlName = packageName + "/" + lastPackageName + ".fxml"; - FXMLLoader loader; - - try{ - ResourceBundle pluginResource = ResourceBundle.getBundle(lastPackageName + "Resources", new Locale(HeapStatsUtils.getLanguage()), pluginClassLoader); - loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName), pluginResource); - } - catch(MissingResourceException e){ - loader = new FXMLLoader(pluginClassLoader.getResource(fxmlName)); - } - - Parent root; - - try { - root = loader.load(); - } - catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - return; - } - - PluginController controller = (PluginController)loader.getController(); - controller.setVeil(veil); - controller.setProgress(progress); - - Tab tab = new Tab(); - tab.setText(controller.getPluginName()); - tab.setContent(root); - tab.setOnSelectionChanged(controller.getOnPluginTabSelected()); - - tabPane.getTabs().add(tab); - - pluginList.put(controller.getPluginName(), controller); - } - - public static WindowController getInstance(){ - return thisController; - } - - @FXML - private void onGCAllClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpGCStatisticsToCSV(false); - } - - @FXML - private void onGCSelectedClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpGCStatisticsToCSV(true); - } - - @FXML - private void onHeapAllClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpClassHistogramToCSV(false); - } - - @FXML - private void onHeapSelectedClick(ActionEvent event) { - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - snapShotController.dumpClassHistogramToCSV(true); - } - - @FXML - private void onSnapShotOpenClick(ActionEvent event) { - Tab snapShotTab = tabPane.getTabs().stream() - .filter(t -> t.getText().equals("SnapShot Data")) - .findAny() - .orElseThrow(() -> new IllegalStateException("SnapShot plugin must be loaded.")); - tabPane.getSelectionModel().select(snapShotTab); - SnapShotController snapShotController = (SnapShotController)getPluginController("SnapShot Data"); - - snapShotController.onSnapshotFileClick(event); - } - - @FXML - private void onLogOpenClick(ActionEvent event) { - Tab snapShotTab = tabPane.getTabs().stream() - .filter(t -> t.getText().equals("Log Data")) - .findAny() - .orElseThrow(() -> new IllegalStateException("Log plugin must be loaded.")); - tabPane.getSelectionModel().select(snapShotTab); - LogController logController = (LogController)getPluginController("Log Data"); - - logController.onLogFileClick(event); - } - - @FXML - private void onThreadRecorderOpenClick(ActionEvent event) { - Tab threadRecorderTab = tabPane.getTabs().stream() - .filter(t -> t.getText().equals("Thread Recorder")) - .findAny() - .orElseThrow(() -> new IllegalStateException("Thread Recorder plugin must be loaded.")); - tabPane.getSelectionModel().select(threadRecorderTab); - ThreadRecorderController threadRecorderController = (ThreadRecorderController)getPluginController("Thread Recorder"); - - threadRecorderController.onOpenBtnClick(event); - } - - private void initializeAboutDialog(){ - FXMLLoader loader = new FXMLLoader(getClass().getResource("/jp/co/ntt/oss/heapstats/aboutDialog.fxml"), HeapStatsUtils.getResourceBundle()); - - try { - loader.load(); - aboutDialogController = (AboutDialogController)loader.getController(); - aboutDialogScene = new Scene(loader.getRoot()); - } - catch (IOException ex) { - HeapStatsUtils.showExceptionDialog(ex); - } - - } - - @Override - public void initialize(URL url, ResourceBundle rb) { - thisController = this; - pluginList = new ConcurrentHashMap<>(); - veil = new Region(); - veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.2)"); - veil.setVisible(false); - - progress = new ProgressIndicator(); - progress.setMaxSize(200.0d, 200.0d); - progress.setVisible(false); - - stackPane.getChildren().add(veil); - stackPane.getChildren().add(progress); - - initializeAboutDialog(); - } - - /** - * Set HostServices to open HowTo page. - * @param hostServices HostServices - */ - protected void setHostServices(HostServices hostServices) { - this.hostServices = hostServices; - } - - private ClassLoader createPluginClassLoader(String appJarString){ - Path appJarPath; - - try{ - appJarPath = Paths.get(appJarString); - } - catch(InvalidPathException e){ - if((appJarString.charAt(0) == '/') && (appJarString.length() > 2)){ // for Windows - appJarPath = Paths.get(appJarString.substring(1)); - } - else{ - throw e; - } - } - - Path libPath = appJarPath.getParent().resolve("lib"); - URL[] jarURLList = null; - - try(DirectoryStream jarPaths = Files.newDirectoryStream(libPath, "*.jar")){ - jarURLList = StreamSupport.stream(jarPaths.spliterator(), false) - .map(new FunctionWrapper<>(p -> p.toUri().toURL())) - .filter(u -> !u.getFile().endsWith("heapstats-core.jar")) - .filter(u -> !u.getFile().endsWith("heapstats-mbean.jar")) - .filter(u -> !u.getFile().endsWith("jgraphx.jar")) - .collect(Collectors.toList()) - .toArray(new URL[0]); - } - catch(IOException ex) { - Logger.getLogger(WindowController.class.getName()).log(Level.SEVERE, null, ex); - } - - return (jarURLList == null) ? WindowController.class.getClassLoader() : new URLClassLoader(jarURLList); - } - - /** - * Load plugins which is defined in heapstats.properties. - */ - public void loadPlugin(){ - String resourceName = "/" + this.getClass().getName().replace('.', '/') + ".class"; - String appJarString = this.getClass().getResource(resourceName).getPath(); - - pluginClassLoader = appJarString.contains("!") ? createPluginClassLoader(appJarString.substring(0, appJarString.indexOf('!')).replaceFirst("file:", "")) - : WindowController.class.getClassLoader(); - - FXMLLoader.setDefaultClassLoader(pluginClassLoader); - - List plugins = HeapStatsUtils.getPlugins(); - plugins.stream().forEach(s -> addPlugin(s)); - - aboutDialogController.setPluginInfo(); - } - - /** - * Get controller instance of plugin. - * - * @param pluginName Plugin name which you want. - * @return Controller of Plugin. If it does not exist, return null. - */ - public PluginController getPluginController(String pluginName){ - return pluginList.get(pluginName); - } - - /** - * Get loaded plugin list. - * - * @return Loaded plugin list. - */ - public Map getPluginList() { - return pluginList; - } - - /** - * Select plugin tab - * - * @param pluginName Name of plugin to active. - */ - public void selectTab(String pluginName) throws IllegalArgumentException{ - Tab target = tabPane.getTabs().stream() - .filter(t -> t.getText().equals(pluginName)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException(pluginName + " is not loaded.")); - tabPane.getSelectionModel().select(target); - } - - public Window getOwner() { - return owner; - } - - public void setOwner(Window owner) { - this.owner = owner; - } - -} diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/PluginController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/PluginController.java Fri Jul 14 12:18:45 2017 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2014-2015 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; - -import java.net.URL; -import java.util.Map; -import java.util.ResourceBundle; -import javafx.beans.value.ChangeListener; -import javafx.concurrent.Task; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.fxml.Initializable; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.layout.Region; -import jp.co.ntt.oss.heapstats.WindowController; - -/** - * Base class for HeapStats FX Analyzer plugin. - * - * @author Yasumasa Suenaga - */ -public abstract class PluginController implements Initializable{ - - /** License indication for GPLv2 */ - public static final String LICENSE_GPL_V2 = "GNU General Public License version 2"; - - /** License indication for BSD License */ - public static final String LICENSE_BSD = "Berkeley Software Distribution License"; - - private Region veil; - - private ProgressIndicator progress; - - public abstract String getPluginName(); - - /** - * Getter of license of this plugin. - * - * @return License of this plugin. - */ - public abstract String getLicense(); - - /** - * Getter of license map which is used by this plugin. - * Key is library name, value is license of library. - * - * @return License of libraryes. - */ - public abstract Map getLibraryLicense(); - - /** - * Event handler when tab of this plugin is selected. - * - * @return Event handler of this plugin. - */ - public abstract EventHandler getOnPluginTabSelected(); - - /** - * Event andler when main window is closed. - * - * @return Event handler of this plugin. - */ - public abstract Runnable getOnCloseRequest(); - - /** - * Setter of veil region. - * This region is used for veiling (e.g. showing progress) - * - * @param veil veiling region. - */ - public void setVeil(Region veil){ - this.veil = veil; - } - - /** - * Setter of progress indicator. - * This region is used for veiling (e.g. showing progress) - * - * @param progress progress indicator. - */ - public void setProgress(ProgressIndicator progress){ - this.progress = progress; - } - - @Override - public void initialize(URL location, ResourceBundle resources) { - } - - /** - * Task binder. - * This method binds veil and progress indicator to task. - * - * @param task Task to be binded. - */ - public void bindTask(Task task){ - veil.visibleProperty().bind(task.runningProperty()); - progress.visibleProperty().bind(task.runningProperty()); - progress.progressProperty().bind(task.progressProperty()); - } - - public void setOnWindowResize(ChangeListener event){ - WindowController.getInstance().getOwner().widthProperty().addListener(event); - WindowController.getInstance().getOwner().heightProperty().addListener(event); - } - - /** - * Set data to another plugin. - * This method will be overrided by each plugins. - * - * @param data Data to set. - * @param select Plugin tab will be actived if this value is true. - */ - public void setData(Object data, boolean select){ - if(select){ - WindowController.getInstance().selectTab(getPluginName()); - } - } - - /** - * This class represents license of libraries which are used by each plugin. - */ - public static class LibraryLicense{ - - private final String pluginName; - - private final String libraryName; - - private final String license; - - public LibraryLicense(String pluginName, String libraryName, String license) { - this.pluginName = pluginName; - this.libraryName = libraryName; - this.license = license; - } - - public String getPluginName() { - return pluginName; - } - - public String getLibraryName() { - return libraryName; - } - - public String getLicense() { - return license; - } - - } - -} diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveController.java Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/JVMLiveController.java Mon Jul 17 23:44:04 2017 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 Yasumasa Suenaga + * 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 @@ -68,7 +68,7 @@ import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.util.Duration; -import jp.co.ntt.oss.heapstats.WindowController; +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; @@ -239,7 +239,7 @@ mbeanController.loadAllConfigs(); Stage dialog = new Stage(StageStyle.UTILITY); - try(InputStream icon = WindowController.class.getResourceAsStream("heapstats-icon.png")){ + try(InputStream icon = MainWindowController.class.getResourceAsStream("heapstats-icon.png")){ dialog.getIcons().add(new Image(icon)); } catch(IOException e){ @@ -305,7 +305,7 @@ new FileChooser.ExtensionFilter("All files", "*.*")); Path hs_err_path = crashList.getSelectionModel().getSelectedItem().getHsErrFile().toPath(); - Optional.ofNullable(dialog.showSaveDialog(WindowController.getInstance().getOwner())) + Optional.ofNullable(dialog.showSaveDialog(MainWindowController.getInstance().getOwner())) .ifPresent(new ConsumerWrapper<>(t -> Files.copy(hs_err_path, t.toPath(), StandardCopyOption.REPLACE_EXISTING))); } diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/HeapStatsMBeanController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/HeapStatsMBeanController.java Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/jvmlive/mbean/HeapStatsMBeanController.java Mon Jul 17 23:44:04 2017 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 Yasumasa Suenaga + * 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 @@ -45,7 +45,7 @@ import javafx.stage.FileChooser; import javafx.util.converter.IntegerStringConverter; import javafx.util.converter.LongStringConverter; -import jp.co.ntt.oss.heapstats.WindowController; +import jp.co.ntt.oss.heapstats.MainWindowController; import jp.co.ntt.oss.heapstats.jmx.JMXHelper; import jp.co.ntt.oss.heapstats.mbean.HeapStatsMBean; import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; @@ -254,7 +254,7 @@ dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("CSV file (*.csv)", "*.csv"), new FileChooser.ExtensionFilter("All files", "*.*")); - File logFile = dialog.showSaveDialog(WindowController.getInstance().getOwner()); + File logFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); if(logFile != null){ try { @@ -274,7 +274,7 @@ dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("SnapShot file (*.dat)", "*.dat"), new FileChooser.ExtensionFilter("All files", "*.*")); - File snapshotFile = dialog.showSaveDialog(WindowController.getInstance().getOwner()); + File snapshotFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); if(snapshotFile != null){ try { diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/log/LogController.java Mon Jul 17 23:44:04 2017 +0900 @@ -34,7 +34,7 @@ import javafx.scene.control.TextField; import javafx.stage.FileChooser; import javafx.stage.FileChooser.ExtensionFilter; -import jp.co.ntt.oss.heapstats.WindowController; +import jp.co.ntt.oss.heapstats.MainWindowController; import jp.co.ntt.oss.heapstats.container.log.ArchiveData; import jp.co.ntt.oss.heapstats.container.log.DiffData; import jp.co.ntt.oss.heapstats.container.log.LogData; @@ -167,7 +167,7 @@ dialog.getExtensionFilters().addAll(new ExtensionFilter("Log file (*.csv)", "*.csv"), new ExtensionFilter("All files", "*.*")); - List logList = dialog.showOpenMultipleDialog(WindowController.getInstance().getOwner()); + List logList = dialog.showOpenMultipleDialog(MainWindowController.getInstance().getOwner()); if (logList != null) { logResourcesController.clearAllItems(); diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/snapshot/SnapShotController.java Mon Jul 17 23:44:04 2017 +0900 @@ -51,7 +51,7 @@ import javafx.scene.shape.Rectangle; import javafx.stage.FileChooser; import javafx.stage.FileChooser.ExtensionFilter; -import jp.co.ntt.oss.heapstats.WindowController; +import jp.co.ntt.oss.heapstats.MainWindowController; import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; import jp.co.ntt.oss.heapstats.container.snapshot.SummaryData; @@ -261,7 +261,7 @@ dialog.getExtensionFilters().addAll(new ExtensionFilter("SnapShot file (*.dat)", "*.dat"), new ExtensionFilter("All files", "*.*")); - List snapshotFileList = dialog.showOpenMultipleDialog(WindowController.getInstance().getOwner()); + List snapshotFileList = dialog.showOpenMultipleDialog(MainWindowController.getInstance().getOwner()); if (snapshotFileList != null) { clearAllItems(); @@ -349,7 +349,7 @@ dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); dialog.getExtensionFilters().addAll(new ExtensionFilter("CSV file (*.csv)", "*.csv"), new ExtensionFilter("All files", "*.*")); - File csvFile = dialog.showSaveDialog(WindowController.getInstance().getOwner()); + File csvFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); if (csvFile != null) { TaskAdapter task = new TaskAdapter<>(new CSVDumpGC(csvFile, isSelected ? currentTarget.get() : snapShotHeaders)); @@ -375,7 +375,7 @@ dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); dialog.getExtensionFilters().addAll(new ExtensionFilter("CSV file (*.csv)", "*.csv"), new ExtensionFilter("All files", "*.*")); - File csvFile = dialog.showSaveDialog(WindowController.getInstance().getOwner()); + File csvFile = dialog.showSaveDialog(MainWindowController.getInstance().getOwner()); if (csvFile != null) { Predicate filter = histogramController.getFilter(); diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/plugin/builtin/threadrecorder/ThreadRecorderController.java Mon Jul 17 23:44:04 2017 +0900 @@ -28,7 +28,7 @@ import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; import javafx.stage.FileChooser; -import jp.co.ntt.oss.heapstats.WindowController; +import jp.co.ntt.oss.heapstats.MainWindowController; import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat; import jp.co.ntt.oss.heapstats.plugin.PluginController; import jp.co.ntt.oss.heapstats.task.ThreadRecordParseTask; @@ -154,7 +154,7 @@ dialog.setInitialDirectory(new File(HeapStatsUtils.getDefaultDirectory())); dialog.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Thread Recorder file (*.htr)", "*.htr"), new FileChooser.ExtensionFilter("All files", "*.*")); - File recorderFile = dialog.showOpenDialog(WindowController.getInstance().getOwner()); + File recorderFile = dialog.showOpenDialog(MainWindowController.getInstance().getOwner()); if(recorderFile != null){ timelineView.getItems().clear(); diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsConfigException.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsConfigException.java Fri Jul 14 12:18:45 2017 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2014-2015 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.utils; - -/** - * Exception class when config loader encounters error. - * This exception shows that user sets invalid value into configuration file (heapstats.properties) . - * @author Yasumasa Suenaga - */ -public class HeapStatsConfigException extends Exception{ - - public HeapStatsConfigException(String message){ - super(message); - } - - public HeapStatsConfigException(String message, Throwable cause){ - super(message, cause); - } - -} diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsUtils.java --- a/analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsUtils.java Fri Jul 14 12:18:45 2017 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,423 +0,0 @@ -/* - * Copyright (C) 2014-2016 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.utils; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Properties; -import java.util.ResourceBundle; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javafx.scene.control.Alert; -import javafx.scene.control.TextArea; -import javafx.scene.paint.Color; -import jp.co.ntt.oss.heapstats.plugin.builtin.jvmlive.JVMLiveController; -import jp.co.ntt.oss.heapstats.plugin.builtin.log.LogController; -import jp.co.ntt.oss.heapstats.plugin.builtin.snapshot.SnapShotController; -import jp.co.ntt.oss.heapstats.plugin.builtin.threadrecorder.ThreadRecorderController; - -/** - * Utility class for HeapStats FX Analyzer. - * - * @author Yasumasa Suenaga - */ -public class HeapStatsUtils { - - /* Rect size in Tooltip for chart legend. */ - public static final double TOOLTIP_LEGEND_RECT_SIZE = 10.0d; - - /* Gap between each controls in Tooltip. */ - public static final double TOOLTIP_GRIDPANE_GAP = 5.0d; - - /* Path of HeapStats home directory. */ - private static Path currentPath = null; - - /* Properties for HeapStats analyzer. */ - private static final Properties prop = new Properties(); - - /* Resource bundle for HeapStats Analyzer. */ - private static ResourceBundle resource; - - /* Cached DatetTimeFormatter */ - private static DateTimeFormatter formatter; - - public static Path getHeapStatsHomeDirectory() { - - if (currentPath == null) { - String appJar = Stream.of(System.getProperty("java.class.path").split(System.getProperty("path.separator"))) - .filter(s -> s.endsWith("heapstats-analyzer.jar")) - .findFirst() - .orElse("."); - currentPath = Paths.get(appJar).toAbsolutePath().getParent(); - } - - return currentPath; - } - - /** - * Load HeapStats property file. - * - * @author Yasumasa Suenaga - * @throws IOException - I/O error to parse property file. - * @throws HeapStatsConfigException - Invalid value. - */ - public static void load() throws IOException, HeapStatsConfigException { - Path properties = Paths.get(getHeapStatsHomeDirectory().toString(), "heapstats.properties"); - - try (InputStream in = Files.newInputStream(properties, StandardOpenOption.READ)) { - prop.load(in); - } catch (NoSuchFileException e) { - // use default values. - } - - /* Load resource bundle */ - resource = ResourceBundle.getBundle("HeapStatsResources"); - - /* Validate values in properties. */ - - /* Language */ - String language = prop.getProperty("language"); - if (language == null) { - prop.setProperty("language", "en"); - } else if (!language.equals("en") && !language.equals("ja")) { - throw new HeapStatsConfigException(resource.getString("invalid.option") + " language=" + language); - } - - /* Load resource bundle again */ - resource = ResourceBundle.getBundle("HeapStatsResources", new Locale(prop.getProperty("language"))); - - /* RankLevel */ - String rankLevelStr = prop.getProperty("ranklevel"); - if (rankLevelStr == null) { - prop.setProperty("ranklevel", "5"); - } else { - try { - Integer.decode(rankLevelStr); - } catch (NumberFormatException e) { - throw new HeapStatsConfigException(resource.getString("invalid.option") + " ranklevel=" + rankLevelStr, e); - } - } - - /* ClassRename */ - /* java.lang.Boolean#parseStinrg() does not throws Throwable. - * If user sets invalid value, Boolean will treat "true" . - * See javadoc of java.lang.Boolean . - */ - if (prop.getProperty("replace") == null) { - prop.setProperty("replace", "false"); - } - - /* Default directory for dialogs. */ - if (prop.getProperty("defaultdir") == null) { - prop.setProperty("defaultdir", getHeapStatsHomeDirectory().toString()); - } else { - /* check if defaultdir exists */ - File defaultDir = new File(prop.getProperty("defaultdir")); - if (!defaultDir.exists() || !defaultDir.isDirectory()) { - prop.setProperty("defaultdir", getHeapStatsHomeDirectory().toString()); - } - } - - /* Log file list to parse. */ - if (prop.getProperty("logfile") == null) { - prop.setProperty("logfile", "redhat-release,cmdline,status,smaps,limits"); - } - - /* Socket endpoint file to parse. */ - if (prop.getProperty("socketend") == null) { - prop.setProperty("socketend", "tcp,udp,tcp6,udp6"); - } - - /* Socket owner file to parse. */ - if (prop.getProperty("owner") == null) { - prop.setProperty("owner", "sockowner"); - } - - /* Background color to draw graphs. */ - String bgColorStr = prop.getProperty("bgcolor"); - if (bgColorStr == null) { - prop.setProperty("bgcolor", "white"); - } else { - try { - Color.web(bgColorStr); - } catch (IllegalArgumentException e) { - throw new HeapStatsConfigException(resource.getString("invalid.option") + " bgcolor=" + bgColorStr, e); - } - } - - /* Java Heap order */ - String heapOrder = prop.getProperty("heaporder_bottom_young"); - if (heapOrder == null) { - prop.setProperty("heaporder", "true"); - } - - /* DateTime format */ - prop.putIfAbsent("datetime_format", "yyyy/MM/dd HH:mm:ss"); - formatter = DateTimeFormatter.ofPattern(prop.getProperty("datetime_format")); - - /* Font size of RefTree */ - String fontsize = prop.getProperty("reftree_fontsize"); - if (fontsize == null) { - prop.setProperty("reftree_fontsize", "11"); - } else { - try { - Integer.decode(fontsize); - } catch (NumberFormatException e) { - throw new HeapStatsConfigException(resource.getString("invalid.option") + " reftree_fontsize=" + fontsize, e); - } - } - - /* Tick marker on X axis */ - if (prop.getProperty("tickmarker") == null) { - prop.setProperty("tickmarker", "false"); - } - - /* TickUnit of X axis */ - String xTickUnitStr = prop.getProperty("x_tickunit"); - if (xTickUnitStr == null) { - prop.setProperty("x_tickunit", "20"); - } else { - try { - Double.parseDouble(xTickUnitStr); - } catch (NumberFormatException e) { - throw new HeapStatsConfigException(resource.getString("invalid.option") + " x_tickunit=" + xTickUnitStr, e); - } - } - - /* Add shutdown hook for saving current settings. */ - Runnable savePropImpl = () -> { - try (OutputStream out = Files.newOutputStream(properties, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { - prop.store(out, null); - } catch (IOException ex) { - Logger.getLogger(HeapStatsUtils.class.getName()).log(Level.SEVERE, null, ex); - } - }; - Runtime.getRuntime().addShutdownHook(new Thread(savePropImpl)); - } - - /** - * Get plugin list. - * - * @return Plugin list. - */ - public static List getPlugins() { - List pluginList = new ArrayList<>(); - pluginList.add(LogController.class.getPackage().getName()); - pluginList.add(SnapShotController.class.getPackage().getName()); - pluginList.add(ThreadRecorderController.class.getPackage().getName()); - pluginList.add(JVMLiveController.class.getPackage().getName()); - pluginList.addAll(Arrays.asList(prop.getProperty("plugins", "").split(";"))); - - return pluginList.stream() - .map(s -> s.trim()) - .filter(s -> s.length() > 0) - .distinct() - .collect(Collectors.toList()); - } - - /** - * Get class name replacement. - * - * @return true if replace (Java-style). - */ - public static boolean getReplaceClassName() { - return Boolean.parseBoolean(prop.getProperty("replace")); - } - - /** - * Get default directory. This value will be overriden when any file is - * opened. - * - * @return Current default directory. - */ - public static String getDefaultDirectory() { - return prop.getProperty("defaultdir"); - } - - /** - * Set default directory. - * - * @param currentDir New current directory. - */ - public static void setDefaultDirectory(String currentDir) { - - if (currentDir != null) { - prop.setProperty("defaultdir", currentDir); - } - - } - - /** - * Get rank level. - * - * @return Rank level. - */ - public static int getRankLevel() { - return Integer.parseInt(prop.getProperty("ranklevel")); - } - - /** - * Set rank level. - * - * @param rankLevel New rank level. - */ - public static void setRankLevel(int rankLevel) { - prop.setProperty("ranklevel", Integer.toString(rankLevel)); - } - - /** - * Get background color of chart. - * - * @return Background color of chart. - */ - public static String getChartBgColor() { - return prop.getProperty("bgcolor"); - } - - /** - * Get whether the bottom of java heap order is young or not. - * - * @return whether the bottom is young or not. - */ - public static boolean getHeapOrder() { - return Boolean.parseBoolean(prop.getProperty("heaporder_bottom_young")); - } - - /** - * Set whethr the bottom of java heap order is young or not. - * - * @param heapOrder whether the bottom is young or not. - */ - public static void setHeapOrder(boolean heapOrder) { - prop.setProperty("heaporder_bottom_young", Boolean.toString(heapOrder)); - } - - /** - * Get language. - * - * @return Language - */ - public static String getLanguage() { - return prop.getProperty("language"); - } - - /** - * Get ResourceBundle. This value depends on getLanguage(). - * - * @return ResourceBundle. - */ - public static ResourceBundle getResourceBundle() { - return ResourceBundle.getBundle("HeapStatsResources", new Locale(getLanguage())); - } - - /** - * Get DataTimeFormatter which is initialized by datetime_format value in heapstats.properties. - * @return Instance of DateTimeFormatter. - */ - public static DateTimeFormatter getDateTimeFormatter(){ - return formatter; - } - - /** - * Get font size of ReferenceTree tab. - * @return fontSize - */ - public static int getFontSizeOfRefTree() { - return Integer.parseInt(prop.getProperty("reftree_fontsize")); - } - - /** - * Get font size of ReferenceTree tab. - * @param fontSize font size - */ - public static void setFontSizeOfRefTree( int fontSize ) { - prop.setProperty("reftree_fontsize", Integer.toString(fontSize)); - } - - /** - * Get the switch to show tick marker on X axis. - * - * @return true if Analyzer should show tick marker. - */ - public static boolean getTickMarkerSwitch() { - return Boolean.parseBoolean(prop.getProperty("tickmarker")); - } - - /** - * Get TickUnit on X axis. - * - * @return TickUnit of X axis. - */ - public static double getXTickUnit(){ - return Double.parseDouble(prop.getProperty("x_tickunit")); - } - - /** - * Convert stack trace to String. - * - * @param e Throwable object to convert. - * - * @return String result of e.printStackTrace() - */ - public static String stackTarceToString(Throwable e) { - String result = null; - - try (StringWriter strWriter = new StringWriter(); - PrintWriter printWriter = new PrintWriter(strWriter)) { - e.printStackTrace(printWriter); - result = strWriter.toString(); - } catch (IOException ex) { - Logger.getLogger(HeapStatsUtils.class.getName()).log(Level.SEVERE, null, ex); - } - - return result; - } - - /** - * Show alert dialog. - * - * @param e Throwable instance to show. - */ - public static void showExceptionDialog(Throwable e) { - TextArea details = new TextArea(HeapStatsUtils.stackTarceToString(e)); - details.setEditable(false); - - Alert dialog = new Alert(Alert.AlertType.ERROR); - dialog.setTitle("Error"); - dialog.setHeaderText(e.getLocalizedMessage()); - dialog.getDialogPane().setExpandableContent(details); - dialog.showAndWait(); - } - -} diff -r f1096afde036 -r be9892e921b3 analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/window.fxml --- a/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/window.fxml Fri Jul 14 12:18:45 2017 +0900 +++ b/analyzer/fx/src/main/resources/jp/co/ntt/oss/heapstats/window.fxml Mon Jul 17 23:44:04 2017 +0900 @@ -22,7 +22,7 @@ - + diff -r f1096afde036 -r be9892e921b3 analyzer/jmx-helper/heapstats-jmx-helper.iml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/jmx-helper/heapstats-jmx-helper.iml Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 analyzer/jmx-helper/heapstats-jmx.iml --- a/analyzer/jmx-helper/heapstats-jmx.iml Fri Jul 14 12:18:45 2017 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 analyzer/plugin-api/heapstats-plugin-api.iml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/plugin-api/heapstats-plugin-api.iml Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff -r f1096afde036 -r be9892e921b3 analyzer/plugin-api/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/plugin-api/pom.xml Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,197 @@ + + + + + + 4.0.0 + jp.co.ntt.oss.heapstats + heapstats-plugin-api + 2.1-SNAPSHOT + jar + + HeapStats Analyzer Plugin API + The API for HeapStats Analyzer plugins + http://icedtea.classpath.org/wiki/HeapStats + + + + GNU General Public License, version 2 + https://www.gnu.org/licenses/gpl-2.0.html + repo + + + + + + Yasumasa Suenaga + yasuenag@gmail.com + icedtea + http://www.icedtea.classpath.org + + + KUBOTA Yuji + kubota.yuji@gmail.com + icedtea + http://www.icedtea.classpath.org + + + + + http://icedtea.classpath.org/hg/heapstats + scm:hg:http://icedtea.classpath.org/hg/heapstats + scm:hg:ssh://icedtea.classpath.org/hg/heapstats + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + UTF-8 + 1.8 + 1.8 + + + + NTT OSS Center + + + + + ${project.groupId} + heapstats-mbean + ${project.version} + + + ${project.groupId} + heapstats-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + + -Xlint:all + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-source-plugin + 2.3 + + + attach-sources + deploy + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + deploy + + jar + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + deploy + deploy + + deploy + + + + + + + + + release-sign-artifacts + + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + diff -r f1096afde036 -r be9892e921b3 analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/WindowController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/WindowController.java Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Nippon Telegraph and Telephone Corporation + * + * 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; + +import javafx.stage.Window; +import jp.co.ntt.oss.heapstats.plugin.PluginController; + +import java.util.Map; + +public interface WindowController { + void selectTab(String pluginName) throws IllegalArgumentException; + Window getOwner(); + Map getPluginList(); +} diff -r f1096afde036 -r be9892e921b3 analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/plugin/PluginController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/plugin/PluginController.java Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,166 @@ +/* + * 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; + +import java.net.URL; +import java.util.Map; +import java.util.ResourceBundle; +import javafx.beans.value.ChangeListener; +import javafx.concurrent.Task; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.fxml.Initializable; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.layout.Region; +import jp.co.ntt.oss.heapstats.utils.HeapStatsUtils; + +/** + * Base class for HeapStats FX Analyzer plugin. + * + * @author Yasumasa Suenaga + */ +public abstract class PluginController implements Initializable{ + + /** License indication for GPLv2 */ + public static final String LICENSE_GPL_V2 = "GNU General Public License version 2"; + + /** License indication for BSD License */ + public static final String LICENSE_BSD = "Berkeley Software Distribution License"; + + private Region veil; + + private ProgressIndicator progress; + + public abstract String getPluginName(); + + /** + * Getter of license of this plugin. + * + * @return License of this plugin. + */ + public abstract String getLicense(); + + /** + * Getter of license map which is used by this plugin. + * Key is library name, value is license of library. + * + * @return License of libraryes. + */ + public abstract Map getLibraryLicense(); + + /** + * Event handler when tab of this plugin is selected. + * + * @return Event handler of this plugin. + */ + public abstract EventHandler getOnPluginTabSelected(); + + /** + * Event andler when main window is closed. + * + * @return Event handler of this plugin. + */ + public abstract Runnable getOnCloseRequest(); + + /** + * Setter of veil region. + * This region is used for veiling (e.g. showing progress) + * + * @param veil veiling region. + */ + public void setVeil(Region veil){ + this.veil = veil; + } + + /** + * Setter of progress indicator. + * This region is used for veiling (e.g. showing progress) + * + * @param progress progress indicator. + */ + public void setProgress(ProgressIndicator progress){ + this.progress = progress; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + } + + /** + * Task binder. + * This method binds veil and progress indicator to task. + * + * @param task Task to be binded. + */ + public void bindTask(Task task){ + veil.visibleProperty().bind(task.runningProperty()); + progress.visibleProperty().bind(task.runningProperty()); + progress.progressProperty().bind(task.progressProperty()); + } + + public void setOnWindowResize(ChangeListener event){ + HeapStatsUtils.getWindowController().getOwner().widthProperty().addListener(event); + HeapStatsUtils.getWindowController().getOwner().heightProperty().addListener(event); + } + + /** + * Set data to another plugin. + * This method will be overrided by each plugins. + * + * @param data Data to set. + * @param select Plugin tab will be actived if this value is true. + */ + public void setData(Object data, boolean select){ + if(select){ + HeapStatsUtils.getWindowController().selectTab(getPluginName()); + } + } + + /** + * This class represents license of libraries which are used by each plugin. + */ + public static class LibraryLicense{ + + private final String pluginName; + + private final String libraryName; + + private final String license; + + public LibraryLicense(String pluginName, String libraryName, String license) { + this.pluginName = pluginName; + this.libraryName = libraryName; + this.license = license; + } + + public String getPluginName() { + return pluginName; + } + + public String getLibraryName() { + return libraryName; + } + + public String getLicense() { + return license; + } + + } + +} diff -r f1096afde036 -r be9892e921b3 analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsConfigException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsConfigException.java Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014-2015 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.utils; + +/** + * Exception class when config loader encounters error. + * This exception shows that user sets invalid value into configuration file (heapstats.properties) . + * @author Yasumasa Suenaga + */ +public class HeapStatsConfigException extends Exception{ + + public HeapStatsConfigException(String message){ + super(message); + } + + public HeapStatsConfigException(String message, Throwable cause){ + super(message, cause); + } + +} diff -r f1096afde036 -r be9892e921b3 analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyzer/plugin-api/src/main/java/jp/co/ntt/oss/heapstats/utils/HeapStatsUtils.java Mon Jul 17 23:44:04 2017 +0900 @@ -0,0 +1,431 @@ +/* + * 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.utils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javafx.scene.control.Alert; +import javafx.scene.control.TextArea; +import javafx.scene.paint.Color; +import jp.co.ntt.oss.heapstats.WindowController; + +/** + * Utility class for HeapStats FX Analyzer. + * + * @author Yasumasa Suenaga + */ +public class HeapStatsUtils { + + /* Rect size in Tooltip for chart legend. */ + public static final double TOOLTIP_LEGEND_RECT_SIZE = 10.0d; + + /* Gap between each controls in Tooltip. */ + public static final double TOOLTIP_GRIDPANE_GAP = 5.0d; + + /* Path of HeapStats home directory. */ + private static Path currentPath = null; + + /* Properties for HeapStats analyzer. */ + private static final Properties prop = new Properties(); + + /* Resource bundle for HeapStats Analyzer. */ + private static ResourceBundle resource; + + /* Cached DateTimeFormatter */ + private static DateTimeFormatter formatter; + + /* Main window controller */ + private static WindowController controller; + + public static Path getHeapStatsHomeDirectory() { + + if (currentPath == null) { + String appJar = Stream.of(System.getProperty("java.class.path").split(System.getProperty("path.separator"))) + .filter(s -> s.endsWith("heapstats-analyzer.jar")) + .findFirst() + .orElse("."); + currentPath = Paths.get(appJar).toAbsolutePath().getParent(); + } + + return currentPath; + } + + /** + * Load HeapStats property file. + * + * @author Yasumasa Suenaga + * @throws IOException - I/O error to parse property file. + * @throws HeapStatsConfigException - Invalid value. + */ + public static void load() throws IOException, HeapStatsConfigException { + Path properties = Paths.get(getHeapStatsHomeDirectory().toString(), "heapstats.properties"); + + try (InputStream in = Files.newInputStream(properties, StandardOpenOption.READ)) { + prop.load(in); + } catch (NoSuchFileException e) { + // use default values. + } + + /* Load resource bundle */ + resource = ResourceBundle.getBundle("HeapStatsResources"); + + /* Validate values in properties. */ + + /* Language */ + String language = prop.getProperty("language"); + if (language == null) { + prop.setProperty("language", "en"); + } else if (!language.equals("en") && !language.equals("ja")) { + throw new HeapStatsConfigException(resource.getString("invalid.option") + " language=" + language); + } + + /* Load resource bundle again */ + resource = ResourceBundle.getBundle("HeapStatsResources", new Locale(prop.getProperty("language"))); + + /* RankLevel */ + String rankLevelStr = prop.getProperty("ranklevel"); + if (rankLevelStr == null) { + prop.setProperty("ranklevel", "5"); + } else { + try { + Integer.decode(rankLevelStr); + } catch (NumberFormatException e) { + throw new HeapStatsConfigException(resource.getString("invalid.option") + " ranklevel=" + rankLevelStr, e); + } + } + + /* ClassRename */ + /* java.lang.Boolean#parseStinrg() does not throws Throwable. + * If user sets invalid value, Boolean will treat "true" . + * See javadoc of java.lang.Boolean . + */ + if (prop.getProperty("replace") == null) { + prop.setProperty("replace", "false"); + } + + /* Default directory for dialogs. */ + if (prop.getProperty("defaultdir") == null) { + prop.setProperty("defaultdir", getHeapStatsHomeDirectory().toString()); + } else { + /* check if defaultdir exists */ + File defaultDir = new File(prop.getProperty("defaultdir")); + if (!defaultDir.exists() || !defaultDir.isDirectory()) { + prop.setProperty("defaultdir", getHeapStatsHomeDirectory().toString()); + } + } + + /* Log file list to parse. */ + if (prop.getProperty("logfile") == null) { + prop.setProperty("logfile", "redhat-release,cmdline,status,smaps,limits"); + } + + /* Socket endpoint file to parse. */ + if (prop.getProperty("socketend") == null) { + prop.setProperty("socketend", "tcp,udp,tcp6,udp6"); + } + + /* Socket owner file to parse. */ + if (prop.getProperty("owner") == null) { + prop.setProperty("owner", "sockowner"); + } + + /* Background color to draw graphs. */ + String bgColorStr = prop.getProperty("bgcolor"); + if (bgColorStr == null) { + prop.setProperty("bgcolor", "white"); + } else { + try { + Color.web(bgColorStr); + } catch (IllegalArgumentException e) { + throw new HeapStatsConfigException(resource.getString("invalid.option") + " bgcolor=" + bgColorStr, e); + } + } + + /* Java Heap order */ + String heapOrder = prop.getProperty("heaporder_bottom_young"); + if (heapOrder == null) { + prop.setProperty("heaporder", "true"); + } + + /* DateTime format */ + prop.putIfAbsent("datetime_format", "yyyy/MM/dd HH:mm:ss"); + formatter = DateTimeFormatter.ofPattern(prop.getProperty("datetime_format")); + + /* Font size of RefTree */ + String fontsize = prop.getProperty("reftree_fontsize"); + if (fontsize == null) { + prop.setProperty("reftree_fontsize", "11"); + } else { + try { + Integer.decode(fontsize); + } catch (NumberFormatException e) { + throw new HeapStatsConfigException(resource.getString("invalid.option") + " reftree_fontsize=" + fontsize, e); + } + } + + /* Tick marker on X axis */ + if (prop.getProperty("tickmarker") == null) { + prop.setProperty("tickmarker", "false"); + } + + /* TickUnit of X axis */ + String xTickUnitStr = prop.getProperty("x_tickunit"); + if (xTickUnitStr == null) { + prop.setProperty("x_tickunit", "20"); + } else { + try { + Double.parseDouble(xTickUnitStr); + } catch (NumberFormatException e) { + throw new HeapStatsConfigException(resource.getString("invalid.option") + " x_tickunit=" + xTickUnitStr, e); + } + } + + /* Add shutdown hook for saving current settings. */ + Runnable savePropImpl = () -> { + try (OutputStream out = Files.newOutputStream(properties, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + prop.store(out, null); + } catch (IOException ex) { + Logger.getLogger(HeapStatsUtils.class.getName()).log(Level.SEVERE, null, ex); + } + }; + Runtime.getRuntime().addShutdownHook(new Thread(savePropImpl)); + } + + /** + * Get plugin list by config. + * + * @return Plugin list. + */ + public static List getPlugins() { + return Arrays.asList(prop.getProperty("plugins", "").split(";")) + .stream() + .map(s -> s.trim()) + .filter(s -> s.length() > 0) + .collect(Collectors.toList()); + } + + /** + * Get class name replacement. + * + * @return true if replace (Java-style). + */ + public static boolean getReplaceClassName() { + return Boolean.parseBoolean(prop.getProperty("replace")); + } + + /** + * Get default directory. This value will be overriden when any file is + * opened. + * + * @return Current default directory. + */ + public static String getDefaultDirectory() { + return prop.getProperty("defaultdir"); + } + + /** + * Set default directory. + * + * @param currentDir New current directory. + */ + public static void setDefaultDirectory(String currentDir) { + + if (currentDir != null) { + prop.setProperty("defaultdir", currentDir); + } + + } + + /** + * Get rank level. + * + * @return Rank level. + */ + public static int getRankLevel() { + return Integer.parseInt(prop.getProperty("ranklevel")); + } + + /** + * Set rank level. + * + * @param rankLevel New rank level. + */ + public static void setRankLevel(int rankLevel) { + prop.setProperty("ranklevel", Integer.toString(rankLevel)); + } + + /** + * Get background color of chart. + * + * @return Background color of chart. + */ + public static String getChartBgColor() { + return prop.getProperty("bgcolor"); + } + + /** + * Get whether the bottom of java heap order is young or not. + * + * @return whether the bottom is young or not. + */ + public static boolean getHeapOrder() { + return Boolean.parseBoolean(prop.getProperty("heaporder_bottom_young")); + } + + /** + * Set whethr the bottom of java heap order is young or not. + * + * @param heapOrder whether the bottom is young or not. + */ + public static void setHeapOrder(boolean heapOrder) { + prop.setProperty("heaporder_bottom_young", Boolean.toString(heapOrder)); + } + + /** + * Get language. + * + * @return Language + */ + public static String getLanguage() { + return prop.getProperty("language"); + } + + /** + * Get ResourceBundle. This value depends on getLanguage(). + * + * @return ResourceBundle. + */ + public static ResourceBundle getResourceBundle() { + return ResourceBundle.getBundle("HeapStatsResources", new Locale(getLanguage())); + } + + /** + * Get DataTimeFormatter which is initialized by datetime_format value in heapstats.properties. + * @return Instance of DateTimeFormatter. + */ + public static DateTimeFormatter getDateTimeFormatter(){ + return formatter; + } + + /** + * Get font size of ReferenceTree tab. + * @return fontSize + */ + public static int getFontSizeOfRefTree() { + return Integer.parseInt(prop.getProperty("reftree_fontsize")); + } + + /** + * Get font size of ReferenceTree tab. + * @param fontSize font size + */ + public static void setFontSizeOfRefTree( int fontSize ) { + prop.setProperty("reftree_fontsize", Integer.toString(fontSize)); + } + + /** + * Get the switch to show tick marker on X axis. + * + * @return true if Analyzer should show tick marker. + */ + public static boolean getTickMarkerSwitch() { + return Boolean.parseBoolean(prop.getProperty("tickmarker")); + } + + /** + * Get TickUnit on X axis. + * + * @return TickUnit of X axis. + */ + public static double getXTickUnit(){ + return Double.parseDouble(prop.getProperty("x_tickunit")); + } + + /** + * Convert stack trace to String. + * + * @param e Throwable object to convert. + * + * @return String result of e.printStackTrace() + */ + public static String stackTraceToString(Throwable e) { + String result = null; + + try (StringWriter strWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(strWriter)) { + e.printStackTrace(printWriter); + result = strWriter.toString(); + } catch (IOException ex) { + Logger.getLogger(HeapStatsUtils.class.getName()).log(Level.SEVERE, null, ex); + } + + return result; + } + + /** + * Show alert dialog. + * + * @param e Throwable instance to show. + */ + public static void showExceptionDialog(Throwable e) { + TextArea details = new TextArea(HeapStatsUtils.stackTraceToString(e)); + details.setEditable(false); + + Alert dialog = new Alert(Alert.AlertType.ERROR); + dialog.setTitle("Error"); + dialog.setHeaderText(e.getLocalizedMessage()); + dialog.getDialogPane().setExpandableContent(details); + dialog.showAndWait(); + } + + /** + * Set an instance of main window controller. + * + * @param cont an instance of WindowController. + */ + public static void setWindowController(WindowController cont) { + controller = cont; + } + + /** + * Get an instance of main window controller. + */ + public static WindowController getWindowController() { + return controller; + } + +} diff -r f1096afde036 -r be9892e921b3 pom.xml --- a/pom.xml Fri Jul 14 12:18:45 2017 +0900 +++ b/pom.xml Mon Jul 17 23:44:04 2017 +0900 @@ -67,6 +67,7 @@ mbean/java analyzer/core analyzer/cli + analyzer/plugin-api analyzer/fx analyzer/jmx-helper diff -r f1096afde036 -r be9892e921b3 specs/heapstats.spec --- a/specs/heapstats.spec Fri Jul 14 12:18:45 2017 +0900 +++ b/specs/heapstats.spec Mon Jul 17 23:44:04 2017 +0900 @@ -162,6 +162,7 @@ %{_libexecdir}/heapstats/filterDefine.xsd %{_libexecdir}/heapstats/heapstats.properties %{_libexecdir}/heapstats/lib/jgraphx.jar +%{_libexecdir}/heapstats/lib/heapstats-plugin-api.jar %endif %changelog