diff --git a/src/main/java/org/levigo/jadice/server/converterclient/gui/clusterhealth/ClusterHealthPaneController.java b/src/main/java/org/levigo/jadice/server/converterclient/gui/clusterhealth/ClusterHealthPaneController.java index 821f1d0..f971668 100644 --- a/src/main/java/org/levigo/jadice/server/converterclient/gui/clusterhealth/ClusterHealthPaneController.java +++ b/src/main/java/org/levigo/jadice/server/converterclient/gui/clusterhealth/ClusterHealthPaneController.java @@ -1,5 +1,11 @@ package org.levigo.jadice.server.converterclient.gui.clusterhealth; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -10,8 +16,11 @@ import org.controlsfx.control.HiddenSidesPane; import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver.ArrowLocation; +import org.controlsfx.dialog.ExceptionDialog; import org.levigo.jadice.server.converterclient.Preferences; +import org.levigo.jadice.server.converterclient.gui.clusterhealth.serialization.Marshaller; import org.levigo.jadice.server.converterclient.gui.clusterhealth.serialization.Marshaller.ClusterHealthDTO; +import org.levigo.jadice.server.converterclient.gui.clusterhealth.serialization.MarshallingException; import org.levigo.jadice.server.converterclient.util.FxScheduler; import org.levigo.jadice.server.converterclient.util.UiUtil; @@ -29,9 +38,11 @@ import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; public class ClusterHealthPaneController { - + private static final Logger LOGGER = Logger.getLogger(ClusterHealthPaneController.class); @FXML @@ -39,16 +50,16 @@ public class ClusterHealthPaneController { @FXML private GridView gridView; - + @FXML private ToggleButton toggleSettingsButton; - + @FXML private Button addInstance; - + @FXML - private HiddenSidesPane hiddenSidePane; - + private HiddenSidesPane hiddenSidePane; + @FXML private Button defineWarnings; @@ -56,37 +67,45 @@ public class ClusterHealthPaneController { private ResourceBundle resources; private final ExecutorService exec = Executors.newWorkStealingPool(); - + private final ClusterHealthDTO settings = Preferences.clusterHealthProperty().getValue(); private final ObservableList controlElements = FXCollections.observableArrayList(); - + private FxScheduler timer; - + private PopOver addInstancePopover; - + private PopOver defineWarningsPopover; + private File lastExportDir; + + private ExtensionFilter settingsExtensionFilter; + @FXML protected void initialize() { UiUtil.configureHomeButton(home); - + loadControlElements(); defineWarningsPopover = initPopoverButton(defineWarnings, "/fxml/ConfigureClusterHealthWarnings.fxml"); addInstancePopover = initPopoverButton(addInstance, "/fxml/AddClusterInstance.fxml"); - - hiddenSidePane.pinnedSideProperty().bind(new When(toggleSettingsButton.selectedProperty()).then(Side.TOP).otherwise((Side) null)); - + + hiddenSidePane.pinnedSideProperty().bind( + new When(toggleSettingsButton.selectedProperty()).then(Side.TOP).otherwise((Side) null)); + gridView.setCellFactory(view -> new StatusControlGridCell()); gridView.setBackground(new Background(new BackgroundFill(Color.WHITE, null, null))); gridView.setItems(controlElements); - + timer = new FxScheduler(this::runUpdate); timer.setExecutionUnit(TimeUnit.MINUTES); // Unit is not configurable yet timer.executionRateProperty().bind(Preferences.clusterHealthProperty().getValue().autoUpdateInterval); timer.startedProperty().bind(Preferences.clusterHealthProperty().getValue().autoUpdateEnabled); + + settingsExtensionFilter = new ExtensionFilter(resources.getString("cluster-health.settings"), + Collections.singletonList("*.json")); } - + private PopOver initPopoverButton(Button button, String fxmlLocation) { Node limits = null; try { @@ -99,7 +118,7 @@ private PopOver initPopoverButton(Button button, String fxmlLocation) { ((Pane) button.getParent()).getChildren().remove(button); return null; } - + final PopOver result = new PopOver(limits); result.setHideOnEscape(true); result.setAutoHide(true); @@ -107,7 +126,7 @@ private PopOver initPopoverButton(Button button, String fxmlLocation) { result.setArrowLocation(ArrowLocation.TOP_RIGHT); return result; } - + private void togglePopOver(PopOver p, Node parent) { if (p == null) { return; @@ -118,35 +137,34 @@ private void togglePopOver(PopOver p, Node parent) { p.show(parent); } } - + @FXML protected void showDefineWarningsPopover() { togglePopOver(defineWarningsPopover, defineWarnings); } - + @FXML protected void showAddInstancePopover() { togglePopOver(addInstancePopover, addInstance); } - + @FXML private void runUpdate() { controlElements.forEach(ce -> runUpdateAsyn(ce)); } - + private void runUpdateAsyn(StatusControl control) { exec.submit(() -> { LOGGER.info("Running update for " + control.getClusterInstance().serverNameProperty().get()); control.getClusterInstance().update(); - } - ); + }); } - + protected void removeClusterInstance(StatusControl controlElement) { settings.instances.remove(controlElements.indexOf(controlElement)); } - + private void loadControlElements() { settings.instances.forEach(instance -> { @@ -171,4 +189,36 @@ private void loadControlElements() { gridView.setHorizontalCellSpacing(tmp); }); } + + @FXML + public void exportSettings() { + FileChooser fc = new FileChooser(); + fc.setTitle(resources.getString("cluster-health.export-settings")); + fc.getExtensionFilters().add(settingsExtensionFilter); + if (lastExportDir != null && lastExportDir.isDirectory()) { + fc.setInitialDirectory(lastExportDir); + } + final File file = fc.showSaveDialog(gridView.getScene().getWindow()); + if (file == null) { + LOGGER.info("Action cancelled by user"); + return; + } + try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { + final String marshalled = Marshaller.getDefault().marshallPrettyPrint(settings); + osw.write(marshalled); + LOGGER.info("Exported settings to " + file); + } catch (MarshallingException | IOException e) { + LOGGER.error("Could not export settings", e); + final ExceptionDialog dialog = new ExceptionDialog(e); + dialog.setTitle(resources.getString("dialogs.cluster-health.export-settings-error.title")); + dialog.setHeaderText(resources.getString("dialogs.cluster-health.export-settings-error.masthead")); + dialog.setContentText( + String.format(resources.getString("dialogs.cluster-health.export-settings-error.message"), file.getAbsolutePath())); + dialog.initOwner(gridView.getScene().getWindow()); + dialog.show(); + } finally { + lastExportDir = file.getParentFile(); + } + } + } diff --git a/src/main/resources/fxml/ClusterHealthPane.fxml b/src/main/resources/fxml/ClusterHealthPane.fxml index 1c9ac4a..32fab7c 100644 --- a/src/main/resources/fxml/ClusterHealthPane.fxml +++ b/src/main/resources/fxml/ClusterHealthPane.fxml @@ -71,12 +71,18 @@ + + + - @@ -86,4 +92,4 @@ - + \ No newline at end of file diff --git a/src/main/resources/i18n/ui-strings.properties b/src/main/resources/i18n/ui-strings.properties index c3a686c..ce9526e 100644 --- a/src/main/resources/i18n/ui-strings.properties +++ b/src/main/resources/i18n/ui-strings.properties @@ -78,11 +78,14 @@ server-log.table.message=Message server-log.table.stacktrace=Stacktrace cluster-health.title=Cluster Health -cluster-health.add-instance=Add Instance +cluster-health.add-instance=Add instance cluster-health.add-instance.hostname=hostname cluster-health.add-instance.port=JMX port number cluster-health.add-instance.add=Add -cluster-health.define-warnings=Define Warnings +cluster-health.define-warnings=Define warnings +cluster-health.settings=Cluster Health Settings +cluster-health.export-settings=Export settings +cluster-health.import-settings=Import settings cluster-health.update-now=Update now cluster-health.rule.server-running=Check if server is running @@ -157,6 +160,11 @@ dialogs.jmx.connection-error.message=Could not connect to JMX dialogs.server-log.connection-error.title=Connection Error dialogs.server-log.connection-error.message=Cannot subscribe to server log +dialogs.cluster-health.export-settings-error.title=Error +dialogs.cluster-health.export-settings-error.masthead=Failed to export settings +# %s will be replaced by file name +dialogs.cluster-health.export-settings-error.message=Could not save %s + dialogs.update.update-available.title=Update Check dialogs.update.update-available.masthead=New version available # %s will be replaced by version numbers