diff --git a/core/framework/src/main/java/org/phoebus/framework/workbench/Locations.java b/core/framework/src/main/java/org/phoebus/framework/workbench/Locations.java index f6002b6b58..a99dc3989d 100644 --- a/core/framework/src/main/java/org/phoebus/framework/workbench/Locations.java +++ b/core/framework/src/main/java/org/phoebus/framework/workbench/Locations.java @@ -40,12 +40,20 @@ public static void initialize() private static void initInstall() throws Exception { + //First force logging level to CONFIG and memorize user level + Level userLoggingLevel = logger.getLevel(); + logger.setLevel(Level.CONFIG); + // Check for location of installation, // i.e. the directory that should contain the lib/ // and doc/ folders. String phoebus_install = System.getProperty(PHOEBUS_INSTALL); + String foundFrom = "$("+ PHOEBUS_INSTALL +") system property"; if (phoebus_install == null) { + //If the environment variable is not set + //Read from the workbench preferences before + foundFrom = "from the framework*.jar archive"; // Determine location of this class // During development in the IDE, it's /some/path/phoebus/core/framework/target/classes // In the product, it's /some/path/lib/framework*.jar @@ -63,21 +71,58 @@ private static void initInstall() throws Exception phoebus_install = path.getAbsolutePath(); System.setProperty(PHOEBUS_INSTALL, phoebus_install); } + if(phoebus_install != null) { + logger.log(Level.CONFIG, "phoebus_install is set to {0} found from {1}", new Object[] {phoebus_install , foundFrom}); + } + //Put back user logging level + logger.setLevel(userLoggingLevel); } private static void initUser() { - String phoebus_user = System.getProperty(PHOEBUS_USER); + //First force logging level to CONFIG and memorize user level + Level userLoggingLevel = logger.getLevel(); + logger.setLevel(Level.CONFIG); + String folder_name_preference = System.getProperty(FOLDER_NAME_PREFERENCE); - if (phoebus_user == null) + String foundFrom = "$("+ FOLDER_NAME_PREFERENCE +") system property"; + if (folder_name_preference == null) { - if (folder_name_preference == null) - { - folder_name_preference = ".phoebus"; + //Test preference folder_name_preference before + folder_name_preference = WorkbenchPreferences.phoebus_folder_name; + foundFrom = "org.phoebus.framework.workbench/phoebus_folder_name preference in settings.ini file"; + if(folder_name_preference == null || folder_name_preference.contains("$(")) {//If it is still null + foundFrom = " default value"; + folder_name_preference = ".phoebus"; } - phoebus_user = new File(System.getProperty("user.home"), folder_name_preference).getAbsolutePath(); - System.setProperty(PHOEBUS_USER, phoebus_user); } + + logger.log(Level.CONFIG, "folder_name_preference is set to {0} found from {1}", new Object[] {folder_name_preference , foundFrom}); + + String userHome = System.getProperty(PHOEBUS_USER); + foundFrom = "$("+ PHOEBUS_USER +") system property"; + if (userHome == null) + { + //Test preference phoebus_user before + File userFile = WorkbenchPreferences.phoebus_user; + if(userFile != null && userFile.exists()) { + foundFrom = "org.phoebus.framework.workbench/phoebus_user preference in settings.ini file"; + userHome = userFile.getAbsolutePath(); + } + else { + foundFrom = "$(user.home) system property"; + userHome = System.getProperty("user.home"); + } + } + + logger.log(Level.CONFIG, "user home is set to {0} found from {1}", new Object[] {userHome , foundFrom}); + + String phoebus_user = new File(userHome, folder_name_preference).getAbsolutePath(); + logger.log(Level.CONFIG, "phoebus_user folder is set to " + phoebus_user); + System.setProperty(PHOEBUS_USER, phoebus_user); + + //Put back user logging level + logger.setLevel(userLoggingLevel); } /** 'Install' location contains the lib/ and doc/ directories. @@ -89,6 +134,10 @@ private static void initUser() */ public static File install() { + String install = System.getProperty(PHOEBUS_INSTALL); + if(install == null) { + initialize(); + } return new File(System.getProperty(PHOEBUS_INSTALL)); } @@ -102,6 +151,10 @@ public static File install() */ public static File user() { + String user = System.getProperty(PHOEBUS_USER); + if(user == null) { + initialize(); + } return new File(System.getProperty(PHOEBUS_USER)); } } diff --git a/core/framework/src/main/java/org/phoebus/framework/workbench/WorkbenchPreferences.java b/core/framework/src/main/java/org/phoebus/framework/workbench/WorkbenchPreferences.java index b4c3348fb6..78e1e4261c 100644 --- a/core/framework/src/main/java/org/phoebus/framework/workbench/WorkbenchPreferences.java +++ b/core/framework/src/main/java/org/phoebus/framework/workbench/WorkbenchPreferences.java @@ -27,6 +27,12 @@ public class WorkbenchPreferences /** directory of external applications */ @Preference public static File external_apps_directory; + /** Phoebus memento folder name default from $(phoebus.folder.name.preference) System property */ + @Preference public static String phoebus_folder_name; + + /** Phoebus user home directory contents memento default from $(phoebus.user) System property */ + @Preference public static File phoebus_user; + /** external applications */ public static final Collection external_apps; diff --git a/core/framework/src/main/resources/workbench_preferences.properties b/core/framework/src/main/resources/workbench_preferences.properties index 944c674d8a..39e800da0b 100644 --- a/core/framework/src/main/resources/workbench_preferences.properties +++ b/core/framework/src/main/resources/workbench_preferences.properties @@ -32,5 +32,13 @@ # external_app_alog=Alignment Log,alog,/path/to/alog_viewer # Directory where external applications are started -# May use system properties -external_apps_directory=$(user.home) \ No newline at end of file +# May use system properties $(user.home) +external_apps_directory=$(user.home) + +# Phoebus folder name by default .phoebus +# May use system properties $(phoebus.folder.name.preference) +phoebus_folder_name=$(phoebus.folder.name.preference) + +# Phoebus user home directory path name by default $HOME or %USERPROFILE% +# May use system properties $(phoebus.folder.name.preference +phoebus_user=$(phoebus.user) diff --git a/core/launcher/src/main/java/org/phoebus/product/Launcher.java b/core/launcher/src/main/java/org/phoebus/product/Launcher.java index 5e5d358a3a..f1cf744d89 100644 --- a/core/launcher/src/main/java/org/phoebus/product/Launcher.java +++ b/core/launcher/src/main/java/org/phoebus/product/Launcher.java @@ -10,6 +10,7 @@ import org.phoebus.ui.application.PhoebusApplication; import java.io.File; +import java.io.InputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.lang.reflect.Method; @@ -26,10 +27,55 @@ @SuppressWarnings("nls") public class Launcher { + private static final String SETTINGS_OPTION = "-settings"; + private static final String LOGGING_OPTION = "-logging"; + private static final String DEFAULT_LOGGING_FILE="/logging.properties"; + private static final String LOGGING_PROP = "java.util.logging.config.file"; + public static void main(final String[] original_args) throws Exception { - LogManager.getLogManager().readConfiguration(Launcher.class.getResourceAsStream("/logging.properties")); - final Logger logger = Logger.getLogger(Launcher.class.getName()); - + // First Handle arguments, potentially not even starting the UI + // settings and logging will define the phoebus.install value if not exist + final List args = new ArrayList<>(List.of(original_args)); + + //Handle logging first + int indexOf = args.indexOf(LOGGING_OPTION); + String loggingFilePath = null; + InputStream loggingStream = null; + String errorLoggingOption = null; + if(indexOf > 0 && indexOf <= args.size()) { + loggingFilePath = args.get(indexOf + 1); + File loggingFile = new File(loggingFilePath); + if(loggingFile.exists()) { + try { + loggingStream = new FileInputStream(loggingFile); + System.setProperty(LOGGING_PROP, loggingFilePath); + } + catch (Exception e) { + loggingStream = null; + //Memorize errorMessage to log the error after logging instanciation + errorLoggingOption = "Cannot read user logging file " + loggingFile + " " + e.getMessage(); + } + } + else { + errorLoggingOption = "User logging file " + loggingFilePath + " not found"; + } + } + + //If no logging found use the default one + if(loggingStream == null) { + loggingFilePath = Launcher.class.getResource(DEFAULT_LOGGING_FILE).getFile(); + loggingStream = Launcher.class.getResourceAsStream(DEFAULT_LOGGING_FILE); + } + + //Load logging configuration + LogManager.getLogManager().readConfiguration(loggingStream); + final Logger logger = Logger.getLogger(Launcher.class.getPackageName()); + logger.log(Level.CONFIG, "Loading logging configuration from " + loggingFilePath); + + if(errorLoggingOption != null) { + logger.log(Level.WARNING, errorLoggingOption); + } + boolean showLaunchError = false; // Can't change default charset, but warn if it's not UTF-8. @@ -46,17 +92,36 @@ public static void main(final String[] original_args) throws Exception { logger.severe("Default charset is " + cs.displayName() + " instead of UTF-8."); logger.severe("Add -D\"file.encoding=UTF-8\" to java command line or JAVA_TOOL_OPTIONS"); } - Locations.initialize(); - // Check for site-specific settings.ini bundled into distribution - // before potentially adding command-line settings. - final File site_settings = new File(Locations.install(), "settings.ini"); - if (site_settings.canRead()) { - logger.log(Level.CONFIG, "Loading settings from " + site_settings); - PropertyPreferenceLoader.load(new FileInputStream(site_settings)); + + //Handle user settings.ini in order to get Locations informations + //as user home directory and phoebus folder name + //Install path will be set on call of install() + //Locations.initialize(); + indexOf = args.indexOf(SETTINGS_OPTION); + File site_settings = null; + if(indexOf > 0 && indexOf <= args.size()) { + String settingsFilePath = args.get(indexOf + 1); + site_settings = new File(settingsFilePath); } - + + if(site_settings == null || !site_settings.exists()) { + // Check for site-specific settings.ini bundled into distribution + // before potentially adding command-line settings. + String settingsError = site_settings != null ? site_settings.getAbsolutePath() + " not found" : "is not defined"; + logger.log(Level.WARNING, "Settings file " + settingsError); + site_settings = new File(Locations.install(), "settings.ini"); + } + + if(site_settings != null && site_settings.exists()) { + logger.info("Loading settings from " + site_settings.getAbsolutePath()); + FileInputStream fileInputStream = new FileInputStream(site_settings); + if (site_settings.getName().endsWith(".xml")) + Preferences.importPreferences(fileInputStream); + else + PropertyPreferenceLoader.load(fileInputStream); + } + // Handle arguments, potentially not even starting the UI - final List args = new ArrayList<>(List.of(original_args)); final Iterator iter = args.iterator(); int port = -1; try { diff --git a/core/ui/src/main/java/org/phoebus/ui/Preferences.java b/core/ui/src/main/java/org/phoebus/ui/Preferences.java index 67b8db93ca..4ca81ee0f3 100644 --- a/core/ui/src/main/java/org/phoebus/ui/Preferences.java +++ b/core/ui/src/main/java/org/phoebus/ui/Preferences.java @@ -43,6 +43,8 @@ public class Preferences @Preference public static String default_save_path; /** layout_dir */ @Preference public static String layout_dir; + /** layout_default */ + @Preference public static String layout_default; /** print_landscape */ @Preference public static boolean print_landscape; /** ok_severity_text_color */ diff --git a/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java b/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java index 3a3851fe4c..0acb2707db 100644 --- a/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java +++ b/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java @@ -325,8 +325,7 @@ private void possiblySelectIniFile(CopyOnWriteArrayList application_para String errorMessage = errorTitleAndErrorMessage.getValue(); logger.log(Level.SEVERE, errorMessage); - - Dialog errorDialog = new Alert(AlertType.ERROR); + Alert errorDialog = new Alert(AlertType.ERROR); errorDialog.setTitle(errorTitle); errorDialog.setHeaderText(errorTitle); errorDialog.setContentText(errorMessage + "\n\n" + Messages.PhoebusWillQuit); @@ -348,7 +347,7 @@ private void possiblySelectIniFile(CopyOnWriteArrayList application_para ObservableList iniFilesInDirectory_ObservableList = FXCollections.observableArrayList(iniFilesInDirectory_List); if (iniFilesInDirectory_List.size() > 0) { - Dialog iniFileSelectionDialog = new Dialog(); + Dialog iniFileSelectionDialog = new Dialog<>(); iniFileSelectionDialog.setTitle(Messages.SelectPhoebusConfiguration); iniFileSelectionDialog.setHeaderText(Messages.SelectPhoebusConfiguration); iniFileSelectionDialog.setGraphic(null); @@ -357,7 +356,7 @@ private void possiblySelectIniFile(CopyOnWriteArrayList application_para iniFileSelectionDialog.setHeight(400); iniFileSelectionDialog.setResizable(false); - ListView listView = new ListView(iniFilesInDirectory_ObservableList); + ListView listView = new ListView<>(iniFilesInDirectory_ObservableList); listView.getSelectionModel().select(0); Runnable setReturnValueAndCloseDialog = () -> { @@ -419,20 +418,20 @@ private void possiblySelectIniFile(CopyOnWriteArrayList application_para PropertyPreferenceLoader.load(selectedFile_FileInputStream); } } catch (Exception exception) { - displayErrorMessageAndQuit.accept(new Pair(Messages.ErrorLoadingPhoebusConfiguration, Messages.ErrorLoadingPhoebusConfiguration + " '" + selectedFile.getAbsolutePath() + "': " + exception.getMessage())); + displayErrorMessageAndQuit.accept(new Pair<>(Messages.ErrorLoadingPhoebusConfiguration, Messages.ErrorLoadingPhoebusConfiguration + " '" + selectedFile.getAbsolutePath() + "': " + exception.getMessage())); } } catch (FileNotFoundException e) { - displayErrorMessageAndQuit.accept(new Pair(Messages.ErrorLoadingPhoebusConfiguration, Messages.ErrorLoadingPhoebusConfiguration + " '" + selectedFile.getAbsolutePath() + "': " + Messages.FileDoesNotExist)); + displayErrorMessageAndQuit.accept(new Pair<>(Messages.ErrorLoadingPhoebusConfiguration, Messages.ErrorLoadingPhoebusConfiguration + " '" + selectedFile.getAbsolutePath() + "': " + Messages.FileDoesNotExist)); } } else { // Selecting a configuration was cancelled either by pressing the "X"-button or by pressing the ESC-key. stop(); } } else { - displayErrorMessageAndQuit.accept(new Pair(Messages.ErrorDuringEvalutationOfTheFlagSelectSettings, Messages.ErrorDuringEvalutationOfTheFlagSelectSettings + ": " + MessageFormat.format(Messages.TheDirectoryDoesNotContainConfigurationFiles, iniFilesLocation_String))); + displayErrorMessageAndQuit.accept(new Pair<>(Messages.ErrorDuringEvalutationOfTheFlagSelectSettings, Messages.ErrorDuringEvalutationOfTheFlagSelectSettings + ": " + MessageFormat.format(Messages.TheDirectoryDoesNotContainConfigurationFiles, iniFilesLocation_String))); } } else { - displayErrorMessageAndQuit.accept(new Pair(Messages.ErrorDuringEvalutationOfTheFlagSelectSettings, Messages.ErrorDuringEvalutationOfTheFlagSelectSettings + ": " + MessageFormat.format(Messages.TheArgumentIsNotADirectory, iniFilesLocation_String))); + displayErrorMessageAndQuit.accept(new Pair<>(Messages.ErrorDuringEvalutationOfTheFlagSelectSettings, Messages.ErrorDuringEvalutationOfTheFlagSelectSettings + ": " + MessageFormat.format(Messages.TheArgumentIsNotADirectory, iniFilesLocation_String))); } } } @@ -561,7 +560,28 @@ private void startUI(final MementoTree memento, final JobMonitor monitor) throws Preferences.ui_monitor_period, TimeUnit.MILLISECONDS); closeAllTabsMenuItem.acceleratorProperty().setValue(closeAllTabsKeyCombination); - + + //Load a custom layout at start if layout_default is defined in preferences + String layoutFileName = Preferences.layout_default; + if(layoutFileName != null && !layoutFileName.isBlank()) { + layoutFileName = !layoutFileName.endsWith(".memento")? layoutFileName + ".memento" :layoutFileName; + String layout_dir = Preferences.layout_dir; + File parentFolder = null; + File user = Locations.user(); + if(layout_dir != null && !layout_dir.isBlank() && !layout_dir.contains("$(")) { + parentFolder = new File(user, layout_dir); + } + if(parentFolder == null) { + parentFolder = user; + } + File layoutFile = new File(parentFolder,layoutFileName ); + if(layoutFile.exists()) { + startLayoutReplacement(layoutFile); + } + else { + logger.log(Level.WARNING, "Layout file " + layoutFileName + " is not found"); + } + } } /** @@ -778,8 +798,9 @@ void createLoadLayoutsMenu() { } // Get every momento file from the configured layout - if (Preferences.layout_dir != null && !Preferences.layout_dir.isBlank()) { - final File layoutDir = new File(Preferences.layout_dir); + String layout_dir = Preferences.layout_dir; + if (layout_dir != null && !layout_dir.isBlank() && !layout_dir.contains("$(")) { + final File layoutDir = new File(Locations.user(), layout_dir); if (layoutDir.exists()) { final File[] systemLayoutFiles = layoutDir.listFiles(); if (systemLayoutFiles != null) { diff --git a/core/ui/src/main/java/org/phoebus/ui/application/SaveLayoutHelper.java b/core/ui/src/main/java/org/phoebus/ui/application/SaveLayoutHelper.java index b933336c9f..babdb53383 100644 --- a/core/ui/src/main/java/org/phoebus/ui/application/SaveLayoutHelper.java +++ b/core/ui/src/main/java/org/phoebus/ui/application/SaveLayoutHelper.java @@ -22,6 +22,7 @@ import org.phoebus.framework.autocomplete.ProposalService; import org.phoebus.framework.jobs.JobManager; import org.phoebus.framework.workbench.Locations; +import org.phoebus.ui.Preferences; import org.phoebus.ui.autocomplete.AutocompleteMenu; import org.phoebus.ui.dialog.DialogHelper; import org.phoebus.ui.docking.DockItem; @@ -156,7 +157,23 @@ private static void positionDialog(final Dialog dialog, Stage stage) private static boolean saveState(List stagesToSave, final String layout) { final String memento_filename = layout + ".memento"; - final File memento_file = new File(Locations.user(), memento_filename); + //Take in account layout dir and add Layout folder in the path + String layout_dir = Preferences.layout_dir; + File user = Locations.user(); + File parentFolder = null; + if(layout_dir != null && !layout_dir.isEmpty() && !layout_dir.contains("$(")) { + parentFolder = new File(user , layout_dir); + if(!parentFolder.exists()) { + parentFolder.mkdir(); + } + } + + if(parentFolder == null) { + parentFolder = user; + } + + final File memento_file = new File(parentFolder, memento_filename); + // File.exists() is blocking in nature. // To combat this the phoebus application maintains a list of *.memento files that are in the default directory. // Check if the file name is in the list, and confirm a file overwrite with the user. diff --git a/core/ui/src/main/resources/phoebus_ui_preferences.properties b/core/ui/src/main/resources/phoebus_ui_preferences.properties index 3c840dd285..c85bc446e4 100644 --- a/core/ui/src/main/resources/phoebus_ui_preferences.properties +++ b/core/ui/src/main/resources/phoebus_ui_preferences.properties @@ -96,6 +96,9 @@ default_save_path= # Set the path to a folder with default layouts layout_dir= +# Set default layout at start +layout_default= + # Compute print scaling in 'landscape' mode? # Landscape mode is generally most suited for printouts # of displays or plots, because the monitor tends to be 'wide'.