Java tutorial
/* * Copyright (c) 2016 eilslabs. * * Distributed under the MIT License (license terms are at https://www.github.com/eilslabs/Roddy/LICENSE.txt). */ package de.dkfz.roddy.client.fxuiclient; import de.dkfz.roddy.Roddy; import de.dkfz.roddy.RunMode; import de.dkfz.roddy.StringConstants; import de.dkfz.roddy.client.fxuiclient.settingsviewer.SettingsViewer; import de.dkfz.roddy.client.rmiclient.RoddyRMIClientConnection; import de.dkfz.roddy.client.rmiclient.RoddyRMIInterfaceImplementation; import de.dkfz.roddy.config.AppConfig; import de.dkfz.roddy.config.ConfigurationFactory; import de.dkfz.roddy.config.InformationalConfigurationContent; import de.dkfz.roddy.core.*; import de.dkfz.roddy.execution.io.ExecutionService; import de.dkfz.roddy.client.fxuiclient.fxdatawrappers.FXDataSetWrapper; import de.dkfz.roddy.client.fxuiclient.fxdatawrappers.FXICCWrapper; import de.dkfz.roddy.client.fxuiclient.fxwrappercontrols.GenericListViewItemCellImplementation; import de.dkfz.roddy.client.fxuiclient.fxwrappercontrols.ProjectTreeItemCellImplementation; import de.dkfz.roddy.client.fxuiclient.fxwrappercontrols.ProjectTreeItemCellListener; import de.dkfz.roddy.execution.jobs.JobState; import de.dkfz.roddy.tools.RoddyConversionHelperMethods; import de.dkfz.roddy.tools.RoddyIOHelperMethods; import javafx.application.Application; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Cursor; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.stage.WindowEvent; import org.apache.commons.io.filefilter.WildcardFileFilter; import java.awt.*; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.*; import java.util.List; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; /** * Controller implementation for RoddyUI.fxml * This is the JavaFX replacement for the RoddyUI class. */ public class RoddyUIController extends BorderPane implements Initializable, ProjectTreeItemCellListener { private static final de.dkfz.roddy.tools.LoggerWrapper logger = de.dkfz.roddy.tools.LoggerWrapper .getLogger(RoddyUIController.class.getSimpleName()); private static final String APP_PROPERTY_LAST_OPEN_PROJECT_PATH = "lastOpenProjectPath"; private static final String APP_PROPERTY_FILTER_ANALYSISID = "projectFilterAnalysisID"; private static final String APP_PROPERTY_FILTER_PROJECTID = "projectFilterProjectID"; private static final String APP_PROPERTY_FILTER_HIDE_UNPROCESSABLE = "projectFilterHideUnprocessable"; private static final String APP_PROPERTY_PROJECT_SETTINGS_OPENED = "titlePaneProjectSettingsOpened"; private static final String APP_PROPERTY_PROJECT_DATASET_FILTER_OPENED = "titlePaneProjectDataSetFilterOpened"; private static final String APP_PROPERTY_PROJECT_DATASET_PROCESSING_OPENED = "titlePaneProjectDataSetProcessingOpened"; private static final String APP_PROPERTY_PROJECT_FILTER_SETTINGS_OPENED = "titlePaneProjectFilterSettingsOpened"; public enum TabType { Common, Project, Dataset } /** * The UI controller is a singleton */ private static RoddyUIController instance; @FXML private Image iconApplicationSpecific; @FXML private Image iconApplicationInformal; @FXML private Image iconProjectSpecific; @FXML private Image iconDatasetSpecific; @FXML private ToggleGroup dataSetFilterViewToggleGroup; @FXML private TextField txtDataSetFilter; @FXML private TreeView projectTree; @FXML private TextField txtProjectFilterByID; @FXML private TextField txtProjectFilterByAnalysis; @FXML private CheckBox cbProjectFilterHideUnprocessable; @FXML private Accordion projectDatasetAccordion; @FXML private TitledPane tpDatasets; @FXML private ListView listViewDataSets; @FXML private ComboBox comboBoxApplicationIniFiles; @FXML private TabPane appTabs; // @FXML // private Tab appTabConfiguration; // @FXML public SettingsViewer appIniViewer; @FXML public ListView listViewOfActiveUITasks; @FXML private VBox vboxAvailableAnalyses; @FXML private VBox vboxProcessingMode; // @FXML // private TextField txtAnalysisInputDirectory; // // @FXML // private TextField txtProjectBaseOutputDirectory; // // @FXML // private TextField txtAnalysisOutputDirectory; @FXML private ConfigurationViewer configurationViewer; @FXML private Label lblMemory; @FXML private ProgressBar pgbMemory; @FXML private ProgressIndicator executionService_activityIndicator; @FXML private Circle stateImage_active; @FXML private Circle stateImage_inactive; @FXML private Circle stateImage_connecting; @FXML private TitledPane tpProjectSettings; @FXML private TitledPane tpProjectDataSetFilter; @FXML public TitledPane tpProjectFilterSettings; private FXICCWrapper currentProjectWrapper; private File currentIniFile; private String currentProject; private String currentAnalysis; private RoddyRMIClientConnection currentConnection; private ObservableList<FXDataSetWrapper> currentListOfDataSets = FXCollections.observableArrayList(); private TreeItem<FXICCWrapper> allProjectTreeItemsRoot = null; private Map<String, Map<FXDataSetWrapper, DataSetView>> openDataSetViews = new HashMap<>(); /** * A map which contains the currently open data set view objects. */ private Map<FXDataSetWrapper, DataSetView> activeDataSetViews = new LinkedHashMap<>(); public static RoddyUIController getMainUIController() { return instance; } @Override public void initialize(URL url, ResourceBundle resourceBundle) { instance = this; txtDataSetFilter.textProperty().addListener((observableValue, s, s2) -> refillListOfDataSets()); comboBoxApplicationIniFiles.getSelectionModel().selectedItemProperty().addListener( (observableValue, old, newValue) -> selectedApplicationIniChanged(newValue.toString())); comboBoxApplicationIniFiles.getItems() .addAll(RoddyIOHelperMethods.readTextFile(getUiSettingsFileForIniFiles())); projectTree.setCellFactory(treeView -> new ProjectTreeItemCellImplementation(projectTree, instance)); projectTree.getSelectionModel().selectedItemProperty() .addListener((observableValue, oldValue, newValue) -> { TreeItem<FXICCWrapper> pWrapper = (TreeItem<FXICCWrapper>) projectTree.getSelectionModel() .getSelectedItem(); if (pWrapper == null) return; changeSelectedProject(pWrapper.getValue()); }); listViewDataSets.setCellFactory(listView -> new GenericListViewItemCellImplementation()); listViewDataSets.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); listViewDataSets.getSelectionModel().selectedItemProperty() .addListener((observableValue, old, newValue) -> selectedDataSetsChanged()); currentListOfDataSets.addListener((ListChangeListener<FXDataSetWrapper>) change -> refillListOfDataSets()); String txtAnalysisIDFilter = Roddy.getApplicationProperty(RunMode.UI, APP_PROPERTY_FILTER_ANALYSISID, StringConstants.EMPTY); String txtIDFilter = Roddy.getApplicationProperty(RunMode.UI, APP_PROPERTY_FILTER_PROJECTID, StringConstants.EMPTY); Boolean filterHideUnprocessable = Boolean.parseBoolean(Roddy.getApplicationProperty(RunMode.UI, APP_PROPERTY_FILTER_HIDE_UNPROCESSABLE, Boolean.FALSE.toString())); txtProjectFilterByAnalysis.setText(txtAnalysisIDFilter); txtProjectFilterByID.setText(txtIDFilter); txtProjectFilterByAnalysis.textProperty() .addListener((observableValue, oldValue, newValue) -> refreshProjectView(null)); txtProjectFilterByID.textProperty() .addListener((observableValue, oldValue, newValue) -> refreshProjectView(null)); cbProjectFilterHideUnprocessable.setSelected(filterHideUnprocessable); txtProjectFilterByAnalysis.textProperty().addListener((observableValue, oldValue, newValue) -> Roddy .setApplicationProperty(RunMode.UI, APP_PROPERTY_FILTER_ANALYSISID, newValue)); txtProjectFilterByID.textProperty().addListener((observableValue, oldValue, newValue) -> Roddy .setApplicationProperty(RunMode.UI, APP_PROPERTY_FILTER_ANALYSISID, newValue)); cbProjectFilterHideUnprocessable.selectedProperty() .addListener((observableValue, aBoolean, newValue) -> Roddy.setApplicationProperty(RunMode.UI, APP_PROPERTY_FILTER_HIDE_UNPROCESSABLE, newValue.toString())); setupTitlePaneExpansionProcessing(tpProjectFilterSettings, APP_PROPERTY_PROJECT_FILTER_SETTINGS_OPENED, Boolean.TRUE); setupTitlePaneExpansionProcessing(tpProjectSettings, APP_PROPERTY_PROJECT_SETTINGS_OPENED, Boolean.TRUE); setupTitlePaneExpansionProcessing(tpProjectDataSetFilter, APP_PROPERTY_PROJECT_DATASET_FILTER_OPENED, Boolean.TRUE); RoddyUITask.activeListOfTasksProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { RoddyUITask.invokeASAP(new Runnable() { @Override public void run() { listViewOfActiveUITasks.getItems().clear(); listViewOfActiveUITasks.getItems().addAll(RoddyUITask.activeListOfTasksProperty().values()); } }, "donttrack::Update tasklist", false); } }); startUIUpdateThread(); } private void startUIUpdateThread() { Thread t = new Thread(() -> { while (App.instance.isRunning) { try { invokeDataUpdate(); // final Runtime runtime = Runtime.getRuntime(); // final double maxMem = 1.0 / (double) runtime.totalMemory(); // // RoddyUITask.invokeLater(() -> { // double number = (runtime.totalMemory() - runtime.freeMemory()) * maxMem; // pgbMemory.setProgress(number); // lblMemory.setText(String.format("%8.0f", number * 100.0) + " %"); // lblMemory.setText("" + runtime.freeMemory() + " / " + runtime.totalMemory() + " / " + runtime.maxMemory()); // }, "app info update"); Thread.sleep(15000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } private void invokeDataUpdate() { // Query open datasets if (currentAnalysis != null) { RoddyRMIClientConnection connection = getRMIConnection(currentAnalysis); for (Object _dsw : listViewDataSets.getItems()) { FXDataSetWrapper dsw = (FXDataSetWrapper) _dsw; connection.queryDataSetState(dsw.getId(), dsw.getAnalysis()); } // Query open data set views for (DataSetView dsv : getListOfOpenDataSetViewsForAnalysis().values()) { dsv.updateDataSetInformation(); } } } private String getUiSettingsFileForIniFiles() { String _f = Roddy.getSettingsDirectory() + "/uiRecentleyUsedIniFiles.lst"; File f = new File(_f); if (!f.exists()) RoddyIOHelperMethods.writeTextFile(f, ""); return _f; } private void stopRMITasks() { for (RoddyRMIClientConnection clientConnection : rmiConnectionPool.values()) { try { clientConnection.closeServer(); } catch (Exception ex) { } } } public static void setupTitlePaneExpansionProcessing(TitledPane tp, String id, Boolean defaultValue) { tp.setExpanded(Boolean.parseBoolean(Roddy.getApplicationProperty(RunMode.UI, id, defaultValue.toString()))); tp.expandedProperty().addListener( (obs, oldV, newV) -> Roddy.setApplicationProperty(RunMode.UI, id, "" + tp.isExpanded())); } public ConfigurationViewer getConfigurationViewer() { return configurationViewer; } /** * Helper method to fill the content of a text field specifically to an input objects type. * If this cannot be done, the default value will be used. */ private void fillTextFieldWithObjectValue(TextField textField, Object o, String alternative) { String text = null; if (o != null) { if (o instanceof File) { text = ((File) o).getAbsolutePath(); } else { text = o.toString(); } } else { text = alternative != null ? alternative : ""; } textField.setText(text); } public void addTab(Pane component, String title, TabType tabType, boolean closable) { Tab t = new Tab(title); t.setStyle("TabHeader" + tabType.name()); t.setClosable(true); t.setContent(component); if (tabType == TabType.Dataset) t.setGraphic(new ImageView(iconDatasetSpecific)); appTabs.getTabs().add(t); } private void setStateImageVisibility(boolean f1, boolean f2, boolean f3) { stateImage_active.setVisible(f1); stateImage_inactive.setVisible(f2); stateImage_connecting.setVisible(f3); } /** * Load an ini file, add it to the list of recently used ini files. * TODO Store this info somewhere! * * @param actionEvent */ public void loadApplicationIniFile(ActionEvent actionEvent) { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Open Resource File"); File iniFile = fileChooser.showOpenDialog(App.instance.primaryStage); if (iniFile != null && iniFile.exists()) { comboBoxApplicationIniFiles.getItems().add(iniFile); selectedApplicationIniChanged(iniFile.getAbsolutePath()); } } private void selectedApplicationIniChanged(String _newIni) { currentIniFile = new File(_newIni); RoddyIOHelperMethods.writeTextFile(getUiSettingsFileForIniFiles(), comboBoxApplicationIniFiles.getItems()); appIniViewer.loadSettingsToScreen(currentIniFile); loadProjects(currentIniFile); } /** * Load a list of available projects related to the current ini file * Load all projects from accessible configuration files. * This is done in a JavaFX Task. * * @param iniFile */ private void loadProjects(File iniFile) { RoddyUITask task = new RoddyUITask<TreeItem<FXICCWrapper>>(UIConstants.UITASK_LOAD_PROJECTS) { @Override public TreeItem<FXICCWrapper> _call() throws Exception { TreeItem<FXICCWrapper> root = new TreeItem<>(null); AppConfig appConfig = new AppConfig(iniFile); String[] allConfigDirectories = appConfig.getProperty("configurationDirectories", "") .split(StringConstants.SPLIT_COMMA); List<File> folders = new LinkedList<>(); for (String s : allConfigDirectories) { File f = new File(s); if (f.exists()) folders.add(f); } ConfigurationFactory.initialize(folders); List<InformationalConfigurationContent> availableProjectConfigurations = ConfigurationFactory .getInstance().getAvailableProjectConfigurations(); availableProjectConfigurations.sort(new Comparator<InformationalConfigurationContent>() { @Override public int compare(InformationalConfigurationContent o1, InformationalConfigurationContent o2) { return o1.name.compareTo(o2.name); } }); loadProjectsRecursivelyFromXMLFiles(root, availableProjectConfigurations); return root; } @Override public void _failed() { logger.log(Level.WARNING, UIConstants.ERRTXT_PROJECTSNOTLOADED, getException()); } @Override protected void _succeeded() { allProjectTreeItemsRoot = valueProperty().get(); refreshProjectView(null); } }; RoddyUITask.runTask(task); } /** * Recursive helper method to load projects from configuration files. */ private void loadProjectsRecursivelyFromXMLFiles(final TreeItem<FXICCWrapper> root, List<InformationalConfigurationContent> availableProjectConfigurations) { int count = 0; String path = Roddy.getApplicationProperty(RunMode.UI, RoddyUIController.APP_PROPERTY_LAST_OPEN_PROJECT_PATH, ""); for (InformationalConfigurationContent icc : availableProjectConfigurations) { FXICCWrapper fpw = new FXICCWrapper(icc, count++); TreeItem<FXICCWrapper> newItem = new TreeItem<>(fpw); root.getChildren().add(newItem); try { Map<String, String> analyses = fpw.getAnalyses(); for (String analysisID : analyses.keySet()) { FXICCWrapper fpwAnalysis = new FXICCWrapper(icc, analysisID, count++); newItem.getChildren().add(new TreeItem<>(fpwAnalysis)); } loadProjectsRecursivelyFromXMLFiles(newItem, icc.getSubContent()); } catch (Exception e) { e.printStackTrace(); } } //Add an expand listener to the topmost nodes if (root.getValue() != null) { for (final TreeItem<FXICCWrapper> treeItem : root.getChildren()) { treeItem.setExpanded(true); } return; } for (final TreeItem<FXICCWrapper> treeItem : root.getChildren()) { treeItem.expandedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean newValue) { if (!newValue) return; //Upon expand close all other nodes. for (TreeItem<FXICCWrapper> sister : root.getChildren()) { if (treeItem == sister) { Roddy.setApplicationProperty(RunMode.UI, RoddyUIController.APP_PROPERTY_LAST_OPEN_PROJECT_PATH, treeItem.getValue().getID()); } else { sister.setExpanded(false); } } } }); if (treeItem.getValue().getID().equals(path)) treeItem.setExpanded(true); } } /** * Called when the selected project is changed. */ private void changeSelectedProject(final FXICCWrapper pWrapper) { projectDatasetAccordion.setExpandedPane(tpDatasets); RoddyUITask.runTask(new RoddyUITask<Void>(UIConstants.UITASK_CHANGE_PROJECT) { List<String> analysesList = null; @Override public Void _call() throws Exception { long t1 = ExecutionService.measureStart(); currentProjectWrapper = pWrapper; currentProject = pWrapper.getID(); long t2 = ExecutionService.measureStart(); ExecutionService.measureStop(t2, UIConstants.UITASK_MP_LOADCONFIGURATION); analysesList = currentProjectWrapper.getICC().getListOfAnalyses(); cleanRMIPool(); ExecutionService.measureStop(t2, UIConstants.UITASK_MP_LOAD_ANALYSIS_LIST); return null; } @Override public void _succeeded() { vboxAvailableAnalyses.getChildren().clear(); ToggleGroup tgAnalyses = new ToggleGroup(); for (String analysis : analysesList) { List fullAnalysisID = ProjectFactory.dissectFullAnalysisID(analysis); String id = analysis.split("[:][:]")[0]; String plugin = fullAnalysisID.size() > 0 ? "\n - " + fullAnalysisID.get(0).toString() : ""; RadioButton rb = new RadioButton(id + plugin); rb.setUserData(analysis); rb.setToggleGroup(tgAnalyses); vboxAvailableAnalyses.getChildren().add(rb); } tgAnalyses.selectedToggleProperty().addListener(new ChangeListener<Toggle>() { @Override public void changed(ObservableValue<? extends Toggle> observableValue, Toggle toggle, Toggle toggle2) { changeSelectedAnalysis((String) toggle2.getUserData()); } }); if (analysesList.size() == 1) { tgAnalyses.getToggles().get(0).setSelected(true); } } }); } /** * This method is called if an analysis hyperlink in the projects tree view was clicked. * * @param pWrapper * @param analysisID */ @Override public void analysisSelected(FXICCWrapper pWrapper, String analysisID) { changeSelectedAnalysis(analysisID); } /** * This method is called when a value in the analyses combo box was selected. * * @param analysis */ private void changeSelectedAnalysis(final String analysis) { RoddyUITask.runTask(new RoddyUITask<Void>(UIConstants.UITASK_ANALYSIS_SELECTED) { @Override public Void _call() throws Exception { long t = ExecutionService.measureStart(); if (currentAnalysis == analysis) //Don't call twice return null; currentAnalysis = analysis; long t2 = ExecutionService.measureStart(); ExecutionService.measureStop(t2, UIConstants.UITASK_ANALYSIS_SELECTED_UPD_CFGVIEWER); return null; } @Override public void _succeeded() { long t = ExecutionService.measureStart(); String string = UIConstants.ERRSTR_PATHNOTFOUND; // Analysis activeAnalysis = currentProject.getAnalysis(currentAnalysis); // fillTextFieldWithObjectValue(txtAnalysisOutputDirectory, activeAnalysis.getOutputAnalysisBaseDirectory(), string); // fillTextFieldWithObjectValue(txtAnalysisInputDirectory, activeAnalysis.getInputBaseDirectory(), string); // fillTextFieldWithObjectValue(txtProjectBaseOutputDirectory, activeAnalysis.getOutputBaseDirectory(), string); // // if (!openDataSetViews.containsKey(analysis)) // openDataSetViews.put(activeAnalysis, new LinkedHashMap<FXDataSetWrapper, DataSetView>()); ExecutionService.measureStop(t, UIConstants.UITASK_ANALYSIS_SELECTED); loadDataSetsForProject(); } }); } private Map<String, ReentrantLock> rmiLocks = new LinkedHashMap<>(); private Map<String, RoddyRMIClientConnection> rmiConnectionPool = new LinkedHashMap<>(); private synchronized void cleanRMIPool() { for (RoddyRMIClientConnection c : rmiConnectionPool.values()) { c.closeServer(); } rmiConnectionPool.clear(); } /** * Check and (re-)open / get an (active) rmi server + connection * * @return */ public RoddyRMIClientConnection getRMIConnection(String analysis) { if (RoddyConversionHelperMethods.isNullOrEmpty(analysis) && !analysis.contains("::")) System.err.println( "Malformed analysis " + analysis + " for getRMIConnection(), needs to be fully specified."); List<String> dissected = ProjectFactory.dissectFullAnalysisID(analysis); String pluginID = dissected.get(0); String shortAnalysisId = analysis.split("[:][:]")[0]; ReentrantLock myLock = null; synchronized (rmiLocks) { if (!rmiLocks.containsKey(pluginID)) { rmiLocks.put(pluginID, new ReentrantLock()); } myLock = rmiLocks.get(pluginID); } RoddyRMIClientConnection connection = null; logger.postAlwaysInfo("Locking for " + analysis); myLock.lock(); try { if (!rmiConnectionPool.containsKey(pluginID) || !rmiConnectionPool.get(pluginID).pingServer()) { logger.postAlwaysInfo("Creating connection for " + analysis); RoddyRMIClientConnection clientConnection = new RoddyRMIClientConnection(); clientConnection.startLocalRoddyRMIServerAndConnect(currentIniFile.getAbsolutePath(), currentProjectWrapper.getName(), shortAnalysisId); rmiConnectionPool.put(pluginID, clientConnection); } logger.postAlwaysInfo("Retrieving connection for " + analysis); connection = rmiConnectionPool.get(pluginID); } finally { myLock.unlock(); } return connection; } /** * Load all datasets for an analysis / project */ private void loadDataSetsForProject() { try { listViewDataSets.getItems().clear(); } catch (Exception e) { logger.severe(e.toString()); } synchronized (currentListOfDataSets) { currentListOfDataSets.clear(); } RoddyUITask.runTask(new RoddyUITask<List<FXDataSetWrapper>>(UIConstants.UITASK_LOAD_PROJECTS_DATASETS) { List<FXDataSetWrapper> dataSetWrappers = new LinkedList<>(); List<List<FXDataSetWrapper>> wrapperPackages = new LinkedList<>(); @Override public List<FXDataSetWrapper> _call() throws Exception { if (currentAnalysis == null) return null; List<FXDataSetWrapper> _dataSetWrappers = new LinkedList<>(); // Run a remote RMI Roddy task and load datasets from the new instance. List<String> dissected = ProjectFactory.dissectFullAnalysisID(currentAnalysis); String shortAnalysisId = currentAnalysis.split("[:][:]")[0]; String pluginID = dissected.get(0); if (RoddyConversionHelperMethods.isNullOrEmpty(pluginID)) return null; RoddyRMIClientConnection clientConnection = getRMIConnection(currentAnalysis); assert clientConnection.pingServer(); List<RoddyRMIInterfaceImplementation.DataSetInfoObject> listOfDatasets = clientConnection .getListOfDatasets(shortAnalysisId); for (RoddyRMIInterfaceImplementation.DataSetInfoObject infoObject : listOfDatasets) { _dataSetWrappers.add(new FXDataSetWrapper(currentProjectWrapper.getName(), shortAnalysisId, currentAnalysis, infoObject.getId(), infoObject.getPath().getAbsolutePath())); } Collections.sort(_dataSetWrappers); dataSetWrappers.clear(); dataSetWrappers.addAll(_dataSetWrappers); return _dataSetWrappers; } @Override public void _succeeded() { listViewDataSets.getItems().clear(); listViewDataSets.getItems().addAll(dataSetWrappers); // for (final FXDataSetWrapper dataSetWrapper : dataSetWrappers) { // // } } }); } private void refillListOfDataSets() { //TODO Think about a good update mechanism Maybe a counter which only allowes update if the count fits? if (!Platform.isFxApplicationThread()) { Platform.runLater(new Runnable() { @Override public void run() { refillListOfDataSets(); } }); return; } List<FXDataSetWrapper> newList; List<FXDataSetWrapper> acceptedItems = new LinkedList<>(); synchronized (currentListOfDataSets) { newList = new LinkedList<>(currentListOfDataSets); } try { WildcardFileFilter wff = new WildcardFileFilter(txtDataSetFilter.getText()); listViewDataSets.getSelectionModel().select(-1); listViewDataSets.getItems().clear(); for (FXDataSetWrapper dsw : newList) { if (wff.accept(new File(dsw.getId()))) { acceptedItems.add(dsw); } } listViewDataSets.getItems().addAll(acceptedItems); } catch (Exception e) { e.printStackTrace(); } } private void selectedDataSetsChanged() { List<FXDataSetWrapper> dataSets = listViewDataSets.getSelectionModel().getSelectedItems(); Map<FXDataSetWrapper, DataSetView> currentListOfViews = getListOfOpenDataSetViewsForAnalysis(); Map<FXDataSetWrapper, DataSetView> newMap = new HashMap<>(); if (currentListOfViews == null) currentListOfViews = new LinkedHashMap<>(); //TODO Rework that. Close all dataset tabs. Not more Tag them with Common / Project / Dataset? for (int i = appTabs.getTabs().size() - 1; i > 0; i--) { Tab appTab = appTabs.getTabs().get(i); if (appTab.isClosable()) appTabs.getTabs().remove(appTab); } activeDataSetViews.clear(); for (FXDataSetWrapper dsw : dataSets) { if (dsw == null) continue; //This case can occur, if the analysis changed. Then dsw can be null which leads to an exception.. if (currentListOfViews.containsKey(dsw)) { newMap.put(dsw, currentListOfViews.get(dsw)); } else { DataSetView dsv = new DataSetView(currentAnalysis, dsw); newMap.put(dsw, dsv); } } List<FXDataSetWrapper> keysSorted = new LinkedList<>(newMap.keySet()); Collections.sort(keysSorted); for (FXDataSetWrapper dsw : keysSorted) { addTab(newMap.get(dsw), dsw.getId(), TabType.Dataset, true); } synchronized (openDataSetViews) { openDataSetViews.put(currentAnalysis, newMap); } appTabs.getSelectionModel().select(1); } private Map<FXDataSetWrapper, DataSetView> getListOfOpenDataSetViewsForAnalysis() { Map<FXDataSetWrapper, DataSetView> currentListOfViews; synchronized (openDataSetViews) { currentListOfViews = openDataSetViews.get(currentAnalysis); } return currentListOfViews; } private void runDataSetsDeferred(boolean rerun, boolean test) { RoddyUITask.runTask(new RoddyUITask(UIConstants.UITASK_RERUN_DATASETS) { private List<RoddyRMIInterfaceImplementation.ExecutionContextInfoObject> executionContextInfoObjects; @Override protected Object _call() throws Exception { RoddyRMIClientConnection rmiConnection = getRMIConnection(currentAnalysis); ObservableList selectedItems = listViewDataSets.getSelectionModel().getSelectedItems(); List<String> datasetIds = new LinkedList<>(); for (Object selectedItem : selectedItems) { FXDataSetWrapper dsw = (FXDataSetWrapper) selectedItem; datasetIds.add(dsw.getId()); } if (rerun) executionContextInfoObjects = rmiConnection.rerun(datasetIds, currentAnalysis, test); else { executionContextInfoObjects = rmiConnection.run(datasetIds, currentAnalysis, test); } return null; } @Override protected void _succeeded() { for (RoddyRMIInterfaceImplementation.ExecutionContextInfoObject contextInfoObject : executionContextInfoObjects) { String dsId = contextInfoObject.getDatasetId(); Map<FXDataSetWrapper, DataSetView> listOfOpenDataSetViewsForAnalysis = getListOfOpenDataSetViewsForAnalysis(); for (DataSetView dataSetWrapper : listOfOpenDataSetViewsForAnalysis.values()) { if (dataSetWrapper.getDataSetId().equals(dsId)) { dataSetWrapper.addContextInfo(contextInfoObject); break; } } } } }); } /** * Executes a workflow for a list of data sets * * @param actionEvent */ public void executeWorkflowForDataSets(ActionEvent actionEvent) { runDataSetsDeferred(false, false); } /** * Reruns several data sets without a check... * * @param actionEvent */ public void rerunWorkflowForDataSets(ActionEvent actionEvent) { runDataSetsDeferred(true, false); } private Map<Long, String> activeExecutionTasks = new LinkedHashMap<>(); private ReentrantLock taskCountLock = new ReentrantLock(); private void switchExecutionStateProcessIndicator() { boolean runningTasks = RoddyUITask.getActiveTaskCount() > 0; executionService_activityIndicator.setVisible(runningTasks); if (runningTasks) App.instance.primaryStage.getScene().setCursor(Cursor.WAIT); else App.instance.primaryStage.getScene().setCursor(Cursor.DEFAULT); } public void dataSetFilterChanged(KeyEvent keyEvent) { try { refillListOfDataSets(); } catch (Exception e) { e.printStackTrace(); } } // @Override public void executionStarted(final long id, final String text) { Platform.runLater(new Runnable() { @Override public void run() { taskCountLock.lock(); activeExecutionTasks.put(id, text); switchExecutionStateProcessIndicator(); taskCountLock.unlock(); } }); } // @Override public void executionFinished(final long id, String text) { Platform.runLater(new Runnable() { @Override public void run() { taskCountLock.lock(); activeExecutionTasks.remove(id); switchExecutionStateProcessIndicator(); taskCountLock.unlock(); } }); } public void refreshDatasetsForWorkflow(ActionEvent actionEvent) { loadDataSetsForProject(); } public void reloadProjectView(ActionEvent actionEvent) { ConfigurationFactory.getInstance().initialize(); loadProjects(currentIniFile); } public void refreshProjectView(ActionEvent actionEvent) { TreeItem<FXICCWrapper> filteredRoot = null; boolean filtersApplied = false; boolean hideUnprocessable = cbProjectFilterHideUnprocessable.isSelected(); String analysisIDFilter = txtProjectFilterByAnalysis.getText(); String idFilter = txtProjectFilterByID.getText(); analysisIDFilter = "*" + analysisIDFilter.trim() + "*"; idFilter = "*" + idFilter.trim() + "*"; filtersApplied = hideUnprocessable; filteredRoot = new TreeItem<>(allProjectTreeItemsRoot.getValue()); TreeItem<FXICCWrapper> root = filteredRoot; for (TreeItem<FXICCWrapper> currentNode : allProjectTreeItemsRoot.getChildren()) { TreeItem<FXICCWrapper> addable = isProcessable(currentNode, hideUnprocessable, idFilter, analysisIDFilter); if (addable != null) root.getChildren().add(addable); } projectTree.setRoot(filteredRoot); } private TreeItem<FXICCWrapper> isProcessable(TreeItem<FXICCWrapper> currentNode, boolean hideUnprocessable, String idFilter, String analysisIDFilter) { WildcardFileFilter wffID = new WildcardFileFilter(idFilter); WildcardFileFilter wffAID = new WildcardFileFilter(analysisIDFilter); FXICCWrapper cWrapper = currentNode.getValue(); boolean isVisible = false; TreeItem<FXICCWrapper> copyOfTreeItem = new TreeItem<>(currentNode.getValue()); copyOfTreeItem.setExpanded(currentNode.isExpanded()); // System.out.println(currentNode.getValue().getID() + " " + currentNode.getChildren().size()); //At first: Check, if the node has children and if one of those children is visible. for (TreeItem<FXICCWrapper> treeItem : currentNode.getChildren()) { TreeItem<FXICCWrapper> childVisible = isProcessable(treeItem, hideUnprocessable, idFilter, analysisIDFilter); if (childVisible != null) copyOfTreeItem.getChildren().add(childVisible); } //If there are no visible children, then check the node itself. if (copyOfTreeItem.getChildren().size() == 0) { // System.out.println(cWrapper.getID()); //Is this a project node or an analysis node? isVisible = wffID.accept(new File(cWrapper.getID())); if (!isVisible) return null; if (cWrapper.isAnalysisWrapper()) { isVisible = wffAID.accept(new File(cWrapper.getAnalysisID())); if (!isVisible) return null; } else { if (hideUnprocessable) { isVisible = false; return null; } else { if (cWrapper.hasAnalyses()) return null; } } // if (isVisible && !cWrapper.hasAnalyses()) { // if (currentNode.getChildren().size() > 0) // isVisible = false; // } } else { isVisible = true; } if (isVisible) return copyOfTreeItem; return null; } public void switchActiveTabToConfiguration() { // appTabs.getSelectionModel().select(appTabConfiguration); } public static class App extends Application { public static final String UI_HEADER = "Roddy - Cluster workflow development and management"; public static final String UI_ICON_16PX = "/imgs/roddy_16.png"; public static final String UI_ICON_32PX = "/imgs/roddy_32.png"; static App instance; private Stage primaryStage; private boolean isRunning; public App() { instance = this; isRunning = true; } @Override public void start(final Stage primaryStage) throws Exception { try { this.primaryStage = primaryStage; Parent root = FXMLLoader.load(getClass().getResource("RoddyUI.fxml")); primaryStage.setTitle(UI_HEADER); primaryStage.getIcons().add(new Image(getClass().getResourceAsStream(UI_ICON_16PX))); primaryStage.getIcons().add(new Image(getClass().getResourceAsStream(UI_ICON_32PX))); SystemColor systemColor = SystemColor.control; primaryStage.setScene(new Scene(root, new Color(systemColor.getRed() / 255.0f, systemColor.getGreen() / 255.0f, systemColor.getBlue() / 255.0f, 1))); primaryStage.show(); primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent windowEvent) { RoddyUIController.getMainUIController().stopRMITasks(); primaryStage.close(); } }); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { try { launch(args); } catch (Exception e) { e.printStackTrace(); } } @Override public void stop() throws Exception { super.stop(); //To change body of overridden methods use File | Settings | File Templates. Roddy.exit(); } public void exit() { primaryStage.close(); isRunning = false; } public boolean isRunning() { return isRunning; } } }