org.bidib.wizard.mvc.backup.controller.BackupController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-client Show documentation
Show all versions of bidibwizard-client Show documentation
jBiDiB BiDiB Wizard Client Application POM
package org.bidib.wizard.mvc.backup.controller;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.swing.SwingUtilities;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.bidib.jbidibc.core.node.ConfigurationVariable;
import org.bidib.jbidibc.core.schema.BidibFactory;
import org.bidib.jbidibc.core.schema.bidib2.BiDiB;
import org.bidib.jbidibc.exchange.vendorcv.VendorCvData;
import org.bidib.jbidibc.exchange.vendorcv.VendorCvFactory;
import org.bidib.jbidibc.messages.helpers.Context;
import org.bidib.jbidibc.messages.helpers.DefaultContext;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.wizard.api.model.Macro;
import org.bidib.wizard.api.model.MacroSaveState;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.NodeListProvider;
import org.bidib.wizard.api.model.NodeProvider;
import org.bidib.wizard.api.model.listener.DefaultNodeListListener;
import org.bidib.wizard.api.model.listener.NodeListListener;
import org.bidib.wizard.api.service.node.NodeService;
import org.bidib.wizard.api.service.node.SwitchingNodeService;
import org.bidib.wizard.api.utils.XmlLocaleUtils;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.client.common.view.DockUtils;
import org.bidib.wizard.client.common.view.cvdef.CvContainer;
import org.bidib.wizard.client.common.view.cvdef.CvDefinitionTreeModelRegistry;
import org.bidib.wizard.client.common.view.statusbar.StatusBar;
import org.bidib.wizard.common.labels.WizardLabelWrapper;
import org.bidib.wizard.common.model.settings.WizardSettingsInterface;
import org.bidib.wizard.common.service.SettingsService;
import org.bidib.wizard.common.utils.SearchPathUtils;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.core.service.ConnectionService;
import org.bidib.wizard.mvc.backup.controller.listener.BackupControllerListener;
import org.bidib.wizard.mvc.backup.model.BackupTableModel;
import org.bidib.wizard.mvc.backup.model.NodeBackupModel;
import org.bidib.wizard.mvc.backup.view.BackupView;
import org.bidib.wizard.mvc.main.view.exchange.NodeExchangeHelper;
import org.bidib.wizard.utils.NodeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockableState;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.RelativeDockablePosition;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
public class BackupController implements BackupControllerListener {
private static final Logger LOGGER = LoggerFactory.getLogger(BackupController.class);
private final DockingDesktop desktop;
private DockableStateChangeListener dockableStateChangeListener;
private final Supplier nodeProviderSupplier;
private final NodeListProvider nodeListProvider;
private BackupView backupView;
private BackupTableModel backupTableModel;
@Autowired
private ConnectionService connectionService;
@Autowired
private NodeService nodeService;
@Autowired
private SwitchingNodeService switchingNodeService;
@Autowired
private StatusBar statusBar;
@Autowired
private SettingsService settingsService;
// registry for CvDefinitionTreeTableModel instances
@Autowired
private CvDefinitionTreeModelRegistry cvDefinitionTreeModelRegistry;
@Autowired
private WizardLabelWrapper wizardLabelWrapper;
private final NodeListListener nodeListListener;
private final ScheduledExecutorService backupWorkers =
Executors
.newScheduledThreadPool(5, new ThreadFactoryBuilder().setNameFormat("backupWorkers-thread-%d").build());
private CompositeDisposable compDispNodes;
public BackupController(final DockingDesktop desktop, final Supplier nodeProviderSupplier,
final NodeListProvider nodeListProvider) {
this.desktop = desktop;
this.nodeProviderSupplier = nodeProviderSupplier;
this.nodeListProvider = nodeListProvider;
// create the nodeList listener
this.nodeListListener = new DefaultNodeListListener() {
@Override
public void listNodeAdded(NodeInterface node) {
LOGGER.info("The nodelist has a new node: {}", node);
nodeNew(node);
}
@Override
public void listNodeRemoved(NodeInterface node) {
LOGGER.info("The nodelist has a node removed: {}", node);
nodeLost(node);
}
};
}
public void start() {
LOGGER.info("Start the BackupController.");
// check if the booster table view is already opened
String searchKey = DockKeys.BACKUP_VIEW;
LOGGER.info("Search for view with key: {}", searchKey);
Dockable view = desktop.getContext().getDockableByKey(searchKey);
if (view != null) {
LOGGER.info("Select the existing backup view.");
DockUtils.selectWindow(view);
return;
}
createDockable();
}
public Dockable createDockable() {
LOGGER.info("Create new BackupView.");
if (this.backupView != null) {
LOGGER.info("Select the existing booster table view.");
DockUtils.selectWindow(this.backupView);
return this.backupView;
}
this.compDispNodes = new CompositeDisposable();
this.backupTableModel = new BackupTableModel((text, duration) -> {
if (duration != null) {
statusBar.setStatusText(text, duration);
}
else {
statusBar.setStatusText(text);
}
});
this.backupView = new BackupView(this, this.backupTableModel, this.settingsService, (text, duration) -> {
if (duration != null) {
statusBar.setStatusText(text, duration);
}
else {
statusBar.setStatusText(text);
}
});
if (desktop.getDockables().length > 1) {
for (DockableState dockableState : desktop.getDockables()) {
Dockable dock = dockableState.getDockable();
if (dock.getDockKey().equals(DockKeys.DOCKKEY_TAB_PANEL)) {
LOGGER.info("Found tabPanel and add backup view next to tabPanel.");
desktop.createTab(dock, this.backupView, 2, true);
break;
}
}
}
else {
LOGGER.info("No dockables found. Add backup view to right.");
desktop.addDockable(this.backupView, RelativeDockablePosition.RIGHT);
}
// register as nodeList listener at the main controller
this.nodeListProvider.addNodeListListener(this.nodeListListener);
this.dockableStateChangeListener = new DockableStateChangeListener() {
@Override
public void dockableStateChanged(DockableStateChangeEvent event) {
if (event.getNewState().getDockable().equals(BackupController.this.backupView)
&& event.getNewState().isClosed()) {
LOGGER.info("BackupView was closed, free resources.");
try {
desktop.removeDockableStateChangeListener(dockableStateChangeListener);
}
catch (Exception ex) {
LOGGER
.warn("Remove dockableStateChangeListener from desktop failed: "
+ dockableStateChangeListener, ex);
}
finally {
dockableStateChangeListener = null;
}
try {
BackupController.this.nodeListProvider
.removeNodeListListener(BackupController.this.nodeListListener);
}
catch (Exception ex) {
LOGGER.warn("Remove nodeList listener failed.", ex);
}
if (BackupController.this.compDispNodes != null) {
LOGGER.info("Dispose the node subscription.");
BackupController.this.compDispNodes.dispose();
BackupController.this.compDispNodes = null;
}
BackupController.this.backupView = null;
BackupController.this.backupTableModel = null;
}
}
};
desktop.addDockableStateChangeListener(this.dockableStateChangeListener);
try {
Disposable disp = connectionService.subscribeConnectionStatusChanges(connectionInfo -> {
if (connectionInfo.getConnectionId().equals(ConnectionRegistry.CONNECTION_ID_MAIN)) {
LOGGER.info("Current state: {}", connectionInfo.getConnectionState());
switch (connectionInfo.getConnectionState().getActualPhase()) {
case CONNECTED:
LOGGER.info("The communication was opened.");
break;
case DISCONNECTED:
LOGGER.info("The communication was closed.");
List nodeBackups = new LinkedList<>(backupTableModel.getNodes());
for (NodeBackupModel nodeBackup : nodeBackups) {
backupTableModel.removeNode(nodeBackup.getNode());
}
break;
default:
break;
}
}
}, error -> {
LOGGER.warn("The connection status change caused an error.", error);
}, () -> {
LOGGER.info("The subscription to connection status changes has completed.");
});
this.compDispNodes.add(disp);
final NodeProvider nodeProvider = this.nodeProviderSupplier.get();
if (CollectionUtils.isNotEmpty(nodeProvider.getNodes())) {
LOGGER.info("Add nodes to backup model.");
final List nodes = new LinkedList<>(nodeProvider.getNodes());
for (NodeInterface node : nodes) {
this.backupTableModel.addNode(node, wizardLabelWrapper);
}
}
}
catch (Exception ex) {
LOGGER.warn("Register controller as connection status listener failed.", ex);
}
return this.backupView;
}
private void nodeNew(final NodeInterface node) {
LOGGER.info("New node in system detected: {}", node);
if (SwingUtilities.isEventDispatchThread()) {
internalNewNode(node);
}
else {
SwingUtilities.invokeLater(() -> internalNewNode(node));
}
}
private void internalNewNode(final NodeInterface node) {
LOGGER.info("Add new node to table: {}", node);
backupTableModel.addNode(node, wizardLabelWrapper);
}
private void nodeLost(final NodeInterface node) {
LOGGER.info("Remove node from model: {}", node);
if (SwingUtilities.isEventDispatchThread()) {
backupTableModel.removeNode(node);
}
else {
SwingUtilities.invokeLater(() -> backupTableModel.removeNode(node));
}
}
@Override
public CompletableFuture performBackup(
final String backupDir, final List nodeBackupModels) {
LOGGER.info("Perform backup, backupDir: {}", backupDir);
final List> futures = new LinkedList<>();
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
final NodeExchangeHelper helper = new NodeExchangeHelper();
final String lang = XmlLocaleUtils.getXmlLocaleVendorCV();
final boolean loadCvs = true;
for (NodeBackupModel nodeBackupModel : nodeBackupModels) {
final CompletableFuture future = new CompletableFuture<>();
// schedule for backup
Callable backup = () -> {
final NodeInterface node = nodeBackupModel.getNode();
LOGGER.info("Perform backup for node: {}", node);
SwingUtilities.invokeLater(() -> nodeBackupModel.setProgress(5));
// load CVs
if (loadCvs) {
LOGGER.info("Load the CVs of the node before export: {}", node);
long uniqueId = node.getUniqueId();
CvContainer cvContainer = cvDefinitionTreeModelRegistry.getCvContainer(uniqueId);
if (cvContainer == null) {
if (node.getVendorCV() == null) {
SwingUtilities.invokeLater(() -> nodeBackupModel.setProgress(7));
// try to load cv definition
loadCvDefinition(node);
}
SwingUtilities.invokeLater(() -> nodeBackupModel.setProgress(20));
if (node.getVendorCV() != null) {
LOGGER
.info(
"No cvContainer found but vendorCV available. Prepare the vendorCV tree for node: {}",
node);
final VendorCvData vendorCV = node.getVendorCV();
cvDefinitionTreeModelRegistry.prepareVendorCVTree(node, vendorCV, false);
cvContainer = cvDefinitionTreeModelRegistry.getCvContainer(uniqueId);
}
}
List configurationVariables = null;
if (cvContainer != null) {
configurationVariables = cvContainer.getConfigVariables();
}
if (CollectionUtils.isNotEmpty(configurationVariables)) {
// use the CV list from the model
configurationVariables =
configurationVariables.stream().distinct().collect(Collectors.toList());
// sort the CV variables by number
ConfigurationVariable.sortCvVariables(configurationVariables);
LOGGER.info("Load CV from node");
try {
// must fetch the CV from the nodes
List queriedConfigurationVariables =
nodeService
.queryConfigVariables(ConnectionRegistry.CONNECTION_ID_MAIN, node,
configurationVariables);
LOGGER.info("Current queried configurationVariables: {}", queriedConfigurationVariables);
node.setConfigVariables(queriedConfigurationVariables);
}
catch (Exception ex) {
LOGGER.warn("Query configuration variables from node failed.", ex);
future.completeExceptionally(ex);
return nodeBackupModel;
}
}
else {
LOGGER.warn("No configuration variables available to load from node: {}", node);
}
}
else {
LOGGER.warn("The CV values of the node are not exported!");
}
// check if the macros are loaded
if (node.hasUnloadedMacros()) {
LOGGER.info("The current node has unloaded macros: {}", node);
SwingUtilities.invokeLater(() -> nodeBackupModel.setProgress(30));
try {
for (Macro macro : node.getMacros()) {
if (macro.getMacroSaveState() != MacroSaveState.PERMANENTLY_STORED_ON_NODE) {
LOGGER.info("Load macro content for macro: {}", macro);
NodeUtils
.loadMacroContentFromNode(ConnectionRegistry.CONNECTION_ID_MAIN, nodeService,
switchingNodeService, node, macro);
}
}
}
catch (Exception ex) {
LOGGER.warn("Load macro content from node failed.", ex);
future.completeExceptionally(ex);
return nodeBackupModel;
}
}
else {
LOGGER.info("The current node has no unloaded macros: {}", node);
}
try {
final BiDiB bidib =
helper.prepareBiDiB(node, cvDefinitionTreeModelRegistry, lang, !loadCvs, wizardLabelWrapper);
String fileName = NodeExchangeHelper.prepareFileName(wizardSettings, node);
LOGGER.info("Prepared backup fileName: {}, backupDir: {}", fileName, backupDir);
SwingUtilities.invokeLater(() -> nodeBackupModel.setProgress(50));
// store bidib to file
File backupFile = new File(backupDir, fileName);
BidibFactory.saveBiDiB(bidib, backupFile, false);
LOGGER.info("Save node state passed, fileName: {}", fileName);
SwingUtilities.invokeLater(() -> nodeBackupModel.setProgress(100));
}
catch (Exception ex) {
LOGGER.warn("Failed to prepare backup and save to file.", ex);
future.completeExceptionally(ex);
return nodeBackupModel;
}
future.complete(nodeBackupModel);
return nodeBackupModel;
};
futures.add(future);
this.backupWorkers.submit(backup);
}
final CompletableFuture[] cfs = futures.toArray(new CompletableFuture[0]);
CompletableFuture