/* * Copyright (C) 2014 Mark Clarke * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package za.co.jumpingbean.gcexplorer.ui; import za.co.jumpingbean.utils.RunningProcessUpdater; import za.co.jumpingbean.utils.Units; import java.awt.AWTEvent; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.LinkedList; import java.util.ResourceBundle; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; 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.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.RadioMenuItem; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.ToggleGroup; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.InputEvent; import javafx.scene.input.InputMethodEvent; import javafx.scene.input.KeyEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.util.Callback; import za.co.jumpingbean.gcexplorer.model.MemoryPool; import za.co.jumpingbean.gcexplorer.model.OldGenMemoryPool; import za.co.jumpingbean.gcexplorer.model.PermGenMemoryPool; import za.co.jumpingbean.gcexplorer.model.SurvivorMemoryPool; import za.co.jumpingbean.gcexplorer.model.UUIDProcess; /** * * @author mark */ public class MainForm implements Initializable { @FXML private MenuItem mnuNewProcess; @FXML private MenuItem mnuNumDataPoints; @FXML private MenuItem mnuAbout; @FXML private MenuItem mnuAttachToExisting; @FXML private TableView<UUIDProcess> tblDetails; @FXML private ToggleGroup unitGroup; @FXML private TableColumn<UUIDProcess, Number> tcEdenCommitted; @FXML private TableColumn<UUIDProcess, Number> tcEdenUsed; @FXML private TableColumn<UUIDProcess, Number> tcEdenMax; @FXML private TableColumn<UUIDProcess, Number> tcEdenFree; @FXML private TableColumn<UUIDProcess, Number> tcSurvivorCommitted; @FXML private TableColumn<UUIDProcess, Number> tcSurvivorUsed; @FXML private TableColumn<UUIDProcess, Number> tcSurvivorMax; @FXML private TableColumn<UUIDProcess, Number> tcSurvivorFree; @FXML private TableColumn<UUIDProcess, Number> tcOldGenCommitted; @FXML private TableColumn<UUIDProcess, Number> tcOldGenUsed; @FXML private TableColumn<UUIDProcess, Number> tcOldGenMax; @FXML private TableColumn<UUIDProcess, Number> tcOldGenFree; @FXML private TableColumn<UUIDProcess, Number> tcPermGenCommitted; @FXML private TableColumn<UUIDProcess, Number> tcPermGenUsed; @FXML private TableColumn<UUIDProcess, Number> tcPermGenMax; @FXML private TableColumn<UUIDProcess, Number> tcPermGenFree; @FXML private TableColumn<UUIDProcess, String> tcDescription; @FXML private RadioMenuItem rdbGB; @FXML private RadioMenuItem rdbKB; @FXML private RadioMenuItem rdbMB; @FXML private RadioMenuItem rdbB; @FXML private TabPane tabPane; private final GCExplorer app; private int numDataPoints = 40; private ObservableList<UUIDProcess> processData = FXCollections.observableArrayList(new ArrayList<>()); MainForm(RunningProcessUpdater statsCollector, GCExplorer app) { this.app = app; } protected void changeUnits(ActionEvent e) { Units unit = (Units) this.unitGroup.getSelectedToggle().getUserData(); app.setUnits(unit); for (Tab tab : tabPane.getTabs()) { ((ProcessViewForm) tab.getUserData()).updateYAxii(unit); } } protected void newProcess(ActionEvent e) { try { Stage stage = new Stage(); FXMLLoader loader = new FXMLLoader( getClass().getResource("launchProcessDialog.fxml") ); loader.setController(new LaunchProcessDialog(app, this)); Parent pane = loader.load(); stage.setScene(new Scene(pane)); stage.setTitle("Start New Process"); stage.initModality(Modality.WINDOW_MODAL); stage.initOwner(this.tblDetails.getParent().getScene().getWindow()); stage.initStyle(StageStyle.UTILITY); // stage.addEventHandler(ActionEvent.ACTION,new EventHandler(){ // @Override // public void handle(Event event) { // ((LaunchProcessDialog)loader.getController()).validateAndRetrieveOptions(new LinkedList<String>()); // event.consume(); // } // }); stage.show(); } catch (IOException ex) { Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex); } } protected void attachToProcess(ActionEvent e) { try { Stage stage = new Stage(); FXMLLoader loader = new FXMLLoader( getClass().getResource("attachExistingProcess.fxml") ); loader.setController(new AttachExistingProcessDialog(app, this)); Parent pane = loader.load(); stage.setScene(new Scene(pane)); stage.setTitle("Attach to Process"); stage.initModality(Modality.WINDOW_MODAL); stage.initOwner(this.tblDetails.getParent().getScene().getWindow()); stage.initStyle(StageStyle.UTILITY); stage.show(); } catch (IOException ex) { Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex); } } @Override public void initialize(URL location, ResourceBundle resources) { this.mnuNewProcess.setOnAction(this::newProcess); this.mnuNumDataPoints.setOnAction(this::showNumDataPointsDialog); this.mnuAttachToExisting.setOnAction(this::attachToProcess); this.mnuAbout.setOnAction(this::showAboutDialog); this.rdbB.setOnAction(this::changeUnits); this.rdbKB.setOnAction(this::changeUnits); this.rdbMB.setOnAction(this::changeUnits); this.rdbGB.setOnAction(this::changeUnits); this.rdbB.setUserData(Units.B); this.rdbKB.setUserData(Units.KB); this.rdbMB.setUserData(Units.MB); this.rdbGB.setUserData(Units.GB); tcDescription.setCellValueFactory(new PropertyValueFactory<>("description")); this.setColumns(EdenMemoryPool.class, tcEdenCommitted, tcEdenFree, tcEdenUsed, tcEdenMax); this.setColumns(SurvivorMemoryPool.class, tcSurvivorCommitted, tcSurvivorFree, tcSurvivorUsed, tcSurvivorMax); this.setColumns(OldGenMemoryPool.class, tcOldGenCommitted, tcOldGenFree, tcOldGenUsed, tcOldGenMax); this.setColumns(PermGenMemoryPool.class, tcPermGenCommitted, tcPermGenFree, tcPermGenUsed, tcPermGenMax); tblDetails.setItems(processData); } private void setColumns(Class<? extends MemoryPool> pool, TableColumn committed, TableColumn free, TableColumn used, TableColumn max) { committed.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<UUIDProcess, Number>, ObservableValue<Number>>() { @Override public ObservableValue<Number> call(TableColumn.CellDataFeatures<UUIDProcess, Number> param) { return param.getValue().getPool(pool).getCommitted().measureProperty(); } }); committed.setCellFactory(new CellNumberFormatter(app)); used.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<UUIDProcess, Number>, ObservableValue<Number>>() { @Override public ObservableValue<Number> call(TableColumn.CellDataFeatures<UUIDProcess, Number> param) { return param.getValue().getPool(pool).getUsed().measureProperty(); } }); used.setCellFactory(new CellNumberFormatter(app)); free.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<UUIDProcess, Number>, ObservableValue<Number>>() { @Override public ObservableValue<Number> call(TableColumn.CellDataFeatures<UUIDProcess, Number> param) { return param.getValue().getPool(pool).getFree().measureProperty(); } }); free.setCellFactory(new CellNumberFormatter(app)); max.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<UUIDProcess, Number>, ObservableValue<Number>>() { @Override public ObservableValue<Number> call(TableColumn.CellDataFeatures<UUIDProcess, Number> param) { return param.getValue().getPool(pool).getMax().measureProperty(); } }); max.setCellFactory(new CellNumberFormatter(app)); } public void addPane(UUID procId, boolean disableGeneratorButton) throws IOException { FXMLLoader loader = new FXMLLoader( ProcessViewForm.class.getResource("processView.fxml") ); ProcessViewForm controller = new ProcessViewForm(app, procId); loader.setController(controller); Parent pane; try { pane = loader.load(); this.addTab(pane, procId, controller); if (disableGeneratorButton) { controller.disableGenerateObjectsButton(); } } catch (IOException ex) { throw new IOException("There was an error adding tab to the main display"); } } private void addTab(Parent pane, UUID procId, ProcessViewForm controller) { Tab tab = new Tab(); tab.setUserData(procId); //tab.setUserData(controller); tab.setOnClosed(new EventHandler<Event>() { @Override public void handle(Event event) { UUID id = (UUID) (tab.getUserData()); processData.remove(app.getProcessController().getUUIDProcess(id)); app.getProcessController().stopProcess(id); } }); tab.setText(app.getProcessController().getProcName(procId)); tab.setContent(pane); tabPane.getTabs().add(tab); this.processData.add(this.app.getProcessController().getUUIDProcess(procId)); } private void showNumDataPointsDialog(ActionEvent e) { Stage stage = new Stage(); VBox vBox = new VBox(); HBox hBox = new HBox(); Button btn = new Button("OK"); Label lbl = new Label("Number of data points?"); TextField txtNum = new TextField(); txtNum.setMaxWidth(50); txtNum.setText(Integer.toString(this.numDataPoints)); hBox.getChildren().add(lbl); hBox.getChildren().add(txtNum); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { String num = txtNum.getText(); try { int curr = numDataPoints; numDataPoints = Integer.parseInt(num); if (numDataPoints > 20 && curr != numDataPoints) { app.getProcessController().updateNumberOfDataPoints(numDataPoints); } stage.close(); } catch (NumberFormatException ex) { } } }); hBox.setPadding(new Insets(0, 0, 10, 0)); vBox.setAlignment(Pos.CENTER); vBox.setPadding(new Insets(10)); vBox.getChildren().add(hBox); vBox.getChildren().add(btn); stage.setScene(new Scene(vBox)); //stage.setTitle("Set Data Set Size"); stage.initModality(Modality.WINDOW_MODAL); stage.initOwner(this.tblDetails.getParent().getScene().getWindow()); stage.initStyle(StageStyle.UTILITY); stage.show(); } public void showAboutDialog(ActionEvent e) { VBox vBox = new VBox(); vBox.setPadding(new Insets(10)); Label jumpingbean = new Label("GCExplorer V0.1 by"); jumpingbean.setPadding(new Insets(10)); jumpingbean.setFont(new Font(14)); Image img = new Image(this.getClass().getResourceAsStream("jumpingbean-logo.png")); ImageView view = new ImageView(img); Hyperlink link = new Hyperlink("www.JumpingBean.biz"); link.setPadding(new Insets(10)); link.setOnAction(new EventHandler() { @Override public void handle(Event event) { app.getHostServices().showDocument("http://www.jumpingbean.biz"); } }); Label about = new Label("GCExplorer was developed as a training aid for Jumping Bean's" + " Java performance tuning training course. It has been released under the GPLv2." + "Jumping Bean is a trademark of Indicento cc"); about.setWrapText(true); about.setMaxWidth(300); vBox.getChildren() .add(jumpingbean); vBox.getChildren() .add(view); vBox.getChildren() .add(link); vBox.getChildren().add(about); vBox.setAlignment(Pos.CENTER); Stage stage = new Stage(); stage.setScene( new Scene(vBox)); stage.initModality(Modality.WINDOW_MODAL); stage.initOwner( this.tblDetails.getParent().getScene().getWindow()); stage.initStyle(StageStyle.UTILITY); stage.show(); } }