/*********************************************************************************** * * Copyright (c) 2014 Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.mqttspy.ui; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.ResourceBundle; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; import javafx.util.Callback; import javafx.util.StringConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.baczkowicz.mqttspy.common.generated.MessageLog; import pl.baczkowicz.mqttspy.configuration.ConfigurationManager; import pl.baczkowicz.mqttspy.configuration.ConfigurationUtils; import pl.baczkowicz.mqttspy.configuration.ConfiguredConnectionDetails; import pl.baczkowicz.mqttspy.configuration.generated.UserInterfaceMqttConnectionDetails; import pl.baczkowicz.mqttspy.connectivity.MqttAsyncConnection; import pl.baczkowicz.mqttspy.ui.connections.ConnectionManager; import pl.baczkowicz.mqttspy.ui.controllers.edit.EditConnectionConnectivityController; import pl.baczkowicz.mqttspy.ui.controllers.edit.EditConnectionLastWillController; import pl.baczkowicz.mqttspy.ui.controllers.edit.EditConnectionMessageLogController; import pl.baczkowicz.mqttspy.ui.controllers.edit.EditConnectionOtherController; import pl.baczkowicz.mqttspy.ui.controllers.edit.EditConnectionPublicationsController; import pl.baczkowicz.mqttspy.ui.controllers.edit.EditConnectionSecurityController; import pl.baczkowicz.mqttspy.ui.controllers.edit.EditConnectionSubscriptionsController; import pl.baczkowicz.mqttspy.ui.utils.ConnectivityUtils; import pl.baczkowicz.mqttspy.utils.ConnectionUtils; import pl.baczkowicz.spy.common.generated.ConnectionGroupReference; import pl.baczkowicz.spy.eventbus.IKBus; import pl.baczkowicz.spy.exceptions.ConfigurationException; import pl.baczkowicz.spy.ui.panes.SpyPerspective; import pl.baczkowicz.spy.ui.utils.DialogFactory; import pl.baczkowicz.spy.ui.utils.TooltipFactory; /** * Controller for editing a single connection. */ @SuppressWarnings({"unchecked", "rawtypes"}) public class EditConnectionController extends AnchorPane implements Initializable { final static Logger logger = LoggerFactory.getLogger(EditConnectionController.class); private SpyPerspective perspective; private List<Tab> tabs; @FXML private TextField connectionNameText; @FXML private ComboBox<SpyPerspective> perspectiveCombo; @FXML private Tab publicationsTab; @FXML private Tab subscriptionsTab; @FXML private Tab otherTab; @FXML private Tab logTab; @FXML private Tab ltwTab; @FXML private TabPane tabPane; // Action buttons @FXML private Button connectButton; @FXML private Button cancelButton; @FXML private Button saveButton; @FXML private Button undoButton; // Controllers @FXML private EditConnectionConnectivityController editConnectionConnectivityController; @FXML private EditConnectionLastWillController editConnectionLastWillController; @FXML private EditConnectionMessageLogController editConnectionMessageLogController; @FXML private EditConnectionOtherController editConnectionOtherController; @FXML private EditConnectionPublicationsController editConnectionPublicationsController; @FXML private EditConnectionSecurityController editConnectionSecurityController; @FXML private EditConnectionSubscriptionsController editConnectionSubscriptionsController; // Other fields private String lastGeneratedConnectionName = ""; private MainController mainController; private ConfiguredConnectionDetails editedConnectionDetails; private boolean recordModifications; private ConfigurationManager configurationManager; private EditConnectionsController editConnectionsController; private boolean openNewMode; private MqttAsyncConnection existingConnection; private int noModificationsLock; private ConnectionManager connectionManager; private boolean emptyConnectionList; private IKBus eventBus; // private final ChangeListener basicOnChangeListener = new ChangeListener() // { // @Override // public void changed(ObservableValue observable, Object oldValue, Object newValue) // { // onChange(); // } // }; // =============================== // === Initialisation ============ // =============================== public void initialize(URL location, ResourceBundle resources) { connectionNameText.textProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { onChange(); } }); editConnectionConnectivityController.setParent(this); editConnectionLastWillController.setParent(this); editConnectionMessageLogController.setParent(this); editConnectionOtherController.setParent(this); editConnectionPublicationsController.setParent(this); editConnectionSecurityController.setParent(this); editConnectionSubscriptionsController.setParent(this); tabs = new ArrayList(tabPane.getTabs()); } private String getPerspectiveString(final SpyPerspective item) { if (item.equals(SpyPerspective.BASIC)) { return ("Basic - the absolute minimum"); } else if (item.equals(SpyPerspective.DEFAULT)) { return ("Default - simpified properties"); } else if (item.equals(SpyPerspective.DETAILED)) { return ("Detailed - all properties"); } else if (item.equals(SpyPerspective.SPY)) { return ("Spy - simplified subscriptions"); } else if (item.equals(SpyPerspective.SUPER_SPY)) { return ("Super Spy - subscriptions only"); } return null; } public void init() { for (SpyPerspective sp : SpyPerspective.values()) { perspectiveCombo.getItems().add(sp); } perspectiveCombo.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { updatePerspective(perspectiveCombo.getSelectionModel().getSelectedItem()); } }); perspectiveCombo.setCellFactory(new Callback<ListView<SpyPerspective>, ListCell<SpyPerspective>>() { @Override public ListCell<SpyPerspective> call(ListView<SpyPerspective> l) { return new ListCell<SpyPerspective>() { @Override protected void updateItem(SpyPerspective item, boolean empty) { super.updateItem(item, empty); if (item == null || empty) { setText(null); } else { setText(getPerspectiveString(item)); } } }; } }); perspectiveCombo.setConverter(new StringConverter<SpyPerspective>() { @Override public String toString(SpyPerspective item) { if (item == null) { return null; } else { return getPerspectiveString(item); } } @Override public SpyPerspective fromString(String id) { return null; } }); editConnectionOtherController.setEventBus(eventBus); editConnectionOtherController.setConfigurationManager(configurationManager); editConnectionConnectivityController.init(); editConnectionLastWillController.init(); editConnectionMessageLogController.init(); editConnectionOtherController.init(); editConnectionPublicationsController.init(); editConnectionSecurityController.init(); editConnectionSubscriptionsController.init(); } private void updatePerspective(final SpyPerspective perspective) { editConnectionConnectivityController.setPerspective(perspective); tabPane.getTabs().clear(); tabPane.getTabs().addAll(tabs); if (perspective.equals(SpyPerspective.BASIC) || perspective.equals(SpyPerspective.SPY) || perspective.equals(SpyPerspective.SUPER_SPY)) { tabPane.getTabs().remove(publicationsTab); } if (perspective.equals(SpyPerspective.BASIC)) { tabPane.getTabs().remove(subscriptionsTab); tabPane.getTabs().remove(otherTab); tabPane.getTabs().remove(logTab); tabPane.getTabs().remove(ltwTab); } // TODO Auto-generated method stub } // =============================== // === FXML ====================== // =============================== @FXML private void addTimestamp() { editConnectionConnectivityController.updateClientId(true); } @FXML private void undo() { editedConnectionDetails.undo(); editConnectionsController.listConnections(); // Note: listing connections should display the existing one updateButtons(); } @FXML private void save() { if (configurationManager.isConfigurationWritable()) { logger.debug("Saving connection " + getConnectionName().getText()); if (configurationManager.saveConfiguration()) { editedConnectionDetails.apply(); editConnectionsController.listConnections(); updateButtons(); TooltipFactory.createTooltip(saveButton, "Changes for connection " + editedConnectionDetails.getName() + " have been saved."); } else { DialogFactory.createErrorDialog( "Cannot save the configuration file", "Oops... an error has occurred while trying to save your configuration. " + "Please check the log file for more information. Your changes were not saved."); } } else { DialogFactory.createErrorDialog( "Cannot save the configuration file", "Oops... your configuration file isn't right. Please restore default configuration. "); } } @FXML private void cancel() { // Get a handle to the stage Stage stage = (Stage) cancelButton.getScene().getWindow(); // Close the window stage.close(); } public void openConnection(final ConfiguredConnectionDetails connectionDetails) { final String validationResult = ConnectivityUtils.validateConnectionDetails(connectionDetails, false); if (validationResult != null) { DialogFactory.createWarningDialog("Invalid value detected", validationResult); } else { if (connectionDetails.isModified()) { Optional<ButtonType> response = DialogFactory.createQuestionDialog( "Unsaved changes detected", "You've got unsaved changes for " + "connection " + connectionDetails.getName() + ". Do you want to save/apply them now?", true); if (response.get() == ButtonType.YES) { save(); } else if (response.get() == ButtonType.NO) { // Do nothing } else { return; } } checkIfOpened(connectionDetails.getID()); if (!openNewMode) { connectionManager.disconnectAndCloseTab(existingConnection); } logger.info("Opening connection " + getConnectionName().getText()); // Get a handle to the stage Stage stage = (Stage) connectButton.getScene().getWindow(); // Close the window stage.close(); Platform.runLater(new Runnable() { @Override public void run() { try { connectionManager.openConnection(connectionDetails, getMainController()); } catch (ConfigurationException e) { // TODO: show warning dialog for invalid logger.error("Cannot open conection {}", connectionDetails.getName(), e); } } }); } } @FXML public void createConnection() throws ConfigurationException { readAndDetectChanges(); openConnection(editedConnectionDetails); } // =============================== // === Logic ===================== // =============================== public void updateConnectionName() { if (connectionNameText.getText().isEmpty() || lastGeneratedConnectionName.equals(connectionNameText.getText())) { final String newName = ConnectionUtils.composeConnectionName( editConnectionConnectivityController.getClientIdText().getText(), editConnectionConnectivityController.getBrokerAddressText().getText()); connectionNameText.setText(newName); lastGeneratedConnectionName = newName; } } public void onChange() { if (recordModifications && !emptyConnectionList) { if (readAndDetectChanges()) { updateButtons(); editConnectionConnectivityController.updateClientId(false); editConnectionConnectivityController.updateClientIdLength(); updateConnectionName(); editConnectionConnectivityController.updateReconnection(); editConnectionSecurityController.updateUserAuthentication(); editConnectionsController.listConnections(); } } } private UserInterfaceMqttConnectionDetails readValues() { final UserInterfaceMqttConnectionDetails connection = new UserInterfaceMqttConnectionDetails(); connection.setMessageLog(new MessageLog()); // Populate the default for the values we don't display / are not used ConfigurationUtils.populateConnectionDefaults(connection); connection.setName(connectionNameText.getText()); editConnectionConnectivityController.readValues(connection); editConnectionOtherController.readValues(connection); editConnectionSecurityController.readValues(connection); editConnectionMessageLogController.readValues(connection); editConnectionPublicationsController.readValues(connection); editConnectionSubscriptionsController.readValues(connection); editConnectionLastWillController.readValues(connection); return connection; } private boolean readAndDetectChanges() { final ConfiguredConnectionDetails connection = new ConfiguredConnectionDetails(null, readValues()); // Copy... final ConnectionGroupReference group = editedConnectionDetails.getGroup(); final String id = editedConnectionDetails.getID(); // Set it.. so that comparison is correct... connection.setGroup(group); connection.setID(id); boolean changed = !connection.equals(editedConnectionDetails.getSavedValues()); logger.debug("Values read. Changed = " + changed); editedConnectionDetails.setModified(changed); editedConnectionDetails.setConnectionDetails(connection); // ... and override the group because this is not read from this pane editedConnectionDetails.setGroup(group); editedConnectionDetails.setID(id); return changed; } public void checkIfOpened(final String id) { openNewMode = true; for (final MqttAsyncConnection mqttConnection : connectionManager.getConnections()) { if (id.equals(mqttConnection.getProperties().getConfiguredProperties().getID()) && mqttConnection.isOpened()) { openNewMode = false; existingConnection = mqttConnection; connectButton.setText("Close and re-open existing connection"); break; } } } public void editConnection(final ConfiguredConnectionDetails connectionDetails) { synchronized (this) { this.editedConnectionDetails = connectionDetails; // Set 'open connection' button mode openNewMode = true; existingConnection = null; connectButton.setText("Open connection"); logger.debug("Editing connection id={} name={}", editedConnectionDetails.getID(), editedConnectionDetails.getName()); checkIfOpened(connectionDetails.getID()); if (editedConnectionDetails.getName().equals( ConnectionUtils.composeConnectionName(editedConnectionDetails.getClientID(), editedConnectionDetails.getServerURI()))) { lastGeneratedConnectionName = editedConnectionDetails.getName(); } else { lastGeneratedConnectionName = ""; } displayConnectionDetails(editedConnectionDetails); editConnectionConnectivityController.updateClientIdLength(); updateConnectionName(); updateButtons(); } } private void updateButtons() { if (editedConnectionDetails != null && editedConnectionDetails.isModified()) { saveButton.setDisable(false); undoButton.setDisable(false); } else { saveButton.setDisable(true); undoButton.setDisable(true); } } private void displayConnectionDetails(final ConfiguredConnectionDetails connection) { ConfigurationUtils.populateConnectionDefaults(connection); connectionNameText.setText(connection.getName()); editConnectionConnectivityController.displayConnectionDetails(connection); editConnectionSecurityController.displayConnectionDetails(connection); editConnectionMessageLogController.displayConnectionDetails(connection); editConnectionOtherController.displayConnectionDetails(connection); editConnectionPublicationsController.displayConnectionDetails(connection); editConnectionSubscriptionsController.displayConnectionDetails(connection); editConnectionLastWillController.displayConnectionDetails(connection); connection.setBeingCreated(false); } // =============================== // === Setters and getters ======= // =============================== public void setMainController(MainController mainController) { this.mainController = mainController; } public void setConfigurationManager(final ConfigurationManager configurationManager) { this.configurationManager = configurationManager; } public void setEditConnectionsController(EditConnectionsController editConnectionsController) { this.editConnectionsController = editConnectionsController; } public void setRecordModifications(boolean recordModifications) { if (!recordModifications) { logger.trace("Modifications suspended..."); noModificationsLock++; this.recordModifications = recordModifications; } else { noModificationsLock--; // Only allow modifications once the parent caller removes the lock if (noModificationsLock == 0) { logger.trace("Modifications restored..."); this.recordModifications = recordModifications; } } } public boolean isRecordModifications() { return recordModifications; } public void setEmptyConnectionListMode(boolean emptyConnectionList) { this.emptyConnectionList = emptyConnectionList; connectButton.setDisable(emptyConnectionList); updateButtons(); } public void setConnectionManager(final ConnectionManager connectionManager) { this.connectionManager = connectionManager; } public TextField getConnectionName() { return connectionNameText; } public ConfiguredConnectionDetails getEditedConnectionDetails() { return editedConnectionDetails; } /** * @return the mainController */ public MainController getMainController() { return mainController; } public SpyPerspective getPerspective() { return perspective; } public void setPerspective(SpyPerspective perspective) { this.perspective = perspective; perspectiveCombo.getSelectionModel().select(perspective); } /** * Sets the event bus. * * @param eventBus the eventBus to set */ public void setEventBus(final IKBus eventBus) { this.eventBus = eventBus; } }