package com.twasyl.slideshowfx.global.configuration;
import com.twasyl.slideshowfx.logs.SlideshowFXHandler;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Properties;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.logging.*;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* This class provides methods for accessing configuration properties.
*
* @author Thierry Wasylczenko
* @version 1.0.0
* @since SlideshowFX 1.0
*/
public class GlobalConfiguration {
private static final Logger LOGGER = Logger.getLogger(GlobalConfiguration.class.getName());
public static final File APPLICATION_DIRECTORY = new File(System.getProperty("user.home"), ".SlideshowFX");
public static final File CONFIG_FILE = new File(APPLICATION_DIRECTORY, ".slideshowfx.configuration.properties");
public static final File PLUGINS_DIRECTORY = new File(APPLICATION_DIRECTORY, "plugins");
/**
* Name of the parameter used to specify if auto saving files is enabled. The value of the parameter is a boolean.
*/
private static final String AUTO_SAVING_ENABLED_PARAMETER = "application.autoSaving.enabled";
/**
* Name of the parameter used to specify the interval for auto saving files. The value of this parameter must be
* given in seconds.
*/
private static final String AUTO_SAVING_INTERVAL_PARAMETER = "application.autoSaving.interval";
/**
* Name of the parameter used to specify whether the temporary files are deleted when the application is exiting.
* The value of the parameter is a boolean.
*/
private static final String TEMPORARY_FILES_DELETION_ON_EXIT_PARAMETER = "application.temporaryFiles.deleteOnExit";
/**
* Name of the parameter used to specify how old can temporary files be before being deleted when exiting the
* application. The value of this parameter must be given in seconds.
*/
private static final String TEMPORARY_FILES_MAX_AGE_PARAMETER = "application.temporaryFiles.maxAge";
/**
* The default {@link Charset} used by the application when writing files, readings files and converting strings.
*/
private static final Charset DEFAULT_CHARSET = UTF_8;
/**
* Name of the parameter for defining the log level.
*/
private static final String LOG_LEVEL_PARAMETER = ".level";
/**
* Name of the parameter for specifying the log handler.
*/
private static final String LOG_HANDLERS_PARAMETER = "handlers";
/**
* Name of the parameter suffix for the specifying the encoding of the log file.
*/
private static final String LOG_ENCODING_SUFFIX = ".encoding";
/**
* Name of the parameter for specifying the file log limit.
*/
private static final String LOG_FILE_LIMIT_PARAMETER = "java.util.logging.FileHandler.limit";
/**
* Name of the parameter for specifying the pattern of the log file name.
*/
private static final String LOG_FILE_PATTERN_PARAMETER = "java.util.logging.FileHandler.pattern";
/**
* Name of the parameter suffix for the log file formatter.
*/
private static final String LOG_FORMATTER_SUFFIX = ".formatter";
/**
* Name of the parameter for specifying if logs must be appended to the log file.
*/
private static final String LOG_FILE_APPEND_PARAMETER = "java.util.logging.FileHandler.append";
/**
* Creates the configuration directory represented by the {@link #APPLICATION_DIRECTORY} variable if it doesn't
* already exist.
* @return {@code true} if the application directory has been created by this method, {@code false} otherwise.
*/
public synchronized static boolean createApplicationDirectory() {
boolean created = false;
if(!APPLICATION_DIRECTORY.exists()) {
created = APPLICATION_DIRECTORY.mkdir();
}
return created;
}
/**
* Creates the plugins directory represented by the {@link #PLUGINS_DIRECTORY} variable if it doesn't
* already exist.
* If parents directories don't exist, this method will not create them and the directory will not be created.
* @return {@code true} if the plugins directory has been created by this method, {@code false} otherwise.
*/
public synchronized static boolean createPluginsDirectory() {
boolean created = false;
if(!PLUGINS_DIRECTORY.exists()) {
created = PLUGINS_DIRECTORY.mkdir();
}
return created;
}
/**
* Creates the configuration file of the application, represented by the {@link #CONFIG_FILE}
* variable if it doesn't already exist.
* @return {@code true} if the configuration file has been created by this method, {@code false} otherwise.
*/
public synchronized static boolean createConfigurationFile() {
boolean created = false;
if(!CONFIG_FILE.exists()) {
try {
created = CONFIG_FILE.createNewFile();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Can not create the configuration file", e);
}
}
return created;
}
/**
* Fill the configuration file with default values if it exists.
*/
public synchronized static void fillConfigurationWithDefaultValue() {
if(CONFIG_FILE.exists()) {
final Properties properties = readAllPropertiesFromConfigurationFile();
if(!properties.containsKey(TEMPORARY_FILES_DELETION_ON_EXIT_PARAMETER)) enableTemporaryFilesDeletionOnExit(true);
if(!properties.containsKey(TEMPORARY_FILES_MAX_AGE_PARAMETER)) setTemporaryFilesMaxAge(7);
if(!properties.containsKey(AUTO_SAVING_ENABLED_PARAMETER)) enableAutoSaving(false);
if(!properties.containsKey(AUTO_SAVING_INTERVAL_PARAMETER)) setAutoSavingInterval(5);
if(!properties.containsKey(LOG_LEVEL_PARAMETER)) setLogLevel(Level.INFO);
if(!properties.containsKey(LOG_HANDLERS_PARAMETER)) setLogHandler(FileHandler.class, SlideshowFXHandler.class);
if(!properties.containsKey(LOG_FILE_APPEND_PARAMETER)) setLogFileAppend(true);
if(!properties.containsKey(FileHandler.class.getName().concat(LOG_ENCODING_SUFFIX))) setLogEncoding(FileHandler.class, UTF_8);
if(!properties.containsKey(FileHandler.class.getName().concat(LOG_FORMATTER_SUFFIX))) setLogFormatter(FileHandler.class, SimpleFormatter.class);
if(!properties.containsKey(LOG_FILE_LIMIT_PARAMETER)) setLogFileLimit(50000);
if(!properties.containsKey(LOG_FILE_PATTERN_PARAMETER)) setLogFilePattern("%h/.SlideshowFX/sfx%g.log");
if(!properties.containsKey(SlideshowFXHandler.class.getName().concat(LOG_ENCODING_SUFFIX))) setLogEncoding(SlideshowFXHandler.class, UTF_8);
if(!properties.containsKey(SlideshowFXHandler.class.getName().concat(LOG_FORMATTER_SUFFIX))) setLogFormatter(SlideshowFXHandler.class, SimpleFormatter.class);
}
}
/**
* Check if the temporary files can be deleted or not. Temporary files can be deleted if the value of the parameter
* {@link #TEMPORARY_FILES_DELETION_ON_EXIT_PARAMETER} is not {@code null] and {@code true} and the value of the
* parameter {@link #TEMPORARY_FILES_MAX_AGE_PARAMETER} is not {@code null}.
* @return {@code true} if the temporary files can be deleted, {@code false} otherwise.
*/
public static boolean canDeleteTemporaryFiles() {
final Boolean deleteTemporaryFilesOnExist = getBooleanProperty(TEMPORARY_FILES_DELETION_ON_EXIT_PARAMETER);
final Long maxAge = getLongProperty(TEMPORARY_FILES_MAX_AGE_PARAMETER);
return deleteTemporaryFilesOnExist != null && deleteTemporaryFilesOnExist && maxAge != null;
}
/**
* Read all properties stored in the configuration file. If no properties are found or if the configuration file
* doesn't exist, an empty object is returned.
* @return The properties stored in the configuration file.
*/
private synchronized static Properties readAllPropertiesFromConfigurationFile() {
final Properties properties = new Properties();
if(CONFIG_FILE.exists()) {
try(final Reader reader = new FileReader(CONFIG_FILE)) {
properties.load(reader);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Can not load configuration file", e);
}
}
return properties;
}
/**
* Writes all properties to the configuration file. If the given properties are null, nothing is performed.
* @param properties The properties to write to the configuration file.
*/
private synchronized static void writeAllPropertiesToConfigurationFile(final Properties properties) {
if(properties != null) {
try (final Writer writer = new FileWriter(CONFIG_FILE)) {
properties.store(writer, "");
writer.flush();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Can not save configuration", e);
}
}
}
/**
* Get a property from the configuration. This methods return {@code null} is the property
* is not found or if the configuration file does not exist.
*
* @param propertyName The name of the property to retrieve.
* @return The value of the property or {@code null} if it is not found or the configuration does not exist.
* @throws java.lang.NullPointerException If the property name is null.
* @throws java.lang.IllegalArgumentException If the property name is empty.
*/
public synchronized static String getProperty(final String propertyName) {
checkPropertyName(propertyName);
String value = null;
if(CONFIG_FILE.exists()) {
final Properties properties = readAllPropertiesFromConfigurationFile();
value = properties.getProperty(propertyName.trim());
}
return value;
}
/**
* Save the given {@code propertyName} and {@code propertyValue} to the configuration.
*
* @param propertyName The name of the property to save.
* @param propertyValue The value of the property to save.
* @throws java.lang.NullPointerException If the name or value of the property is null.
* @throws java.lang.IllegalArgumentException If the name or value of the property is empty.
*/
public synchronized static void setProperty(final String propertyName, final String propertyValue) {
checkPropertyName(propertyName);
checkPropertyValue(propertyValue);
final Properties properties = readAllPropertiesFromConfigurationFile();
properties.put(propertyName.trim(), propertyValue);
writeAllPropertiesToConfigurationFile(properties);
}
/**
* Remove a property from the configuration file. If the property doesn't exist, nothing is performed.
* @param propertyName The name of the property to remove.
*/
public synchronized static void removeProperty(final String propertyName) {
checkPropertyName(propertyName);
final Properties properties = readAllPropertiesFromConfigurationFile();
if(properties.containsKey(propertyName.trim())) {
properties.remove(propertyName.trim());
writeAllPropertiesToConfigurationFile(properties);
}
}
/**
* Check if the given property name is valid or not. The property name is considered valid if if is not {@code null}
* and its value is not empty.
* @param propertyName The name of the property to check.
* @throws java.lang.NullPointerException If the property name is {@code null}.
* @throws java.lang.IllegalArgumentException If the property name is empty.
*/
private static void checkPropertyName(final String propertyName) {
if(propertyName == null) throw new NullPointerException("The property name can not be null");
if(propertyName.trim().isEmpty()) throw new IllegalArgumentException("The property name can not be empty");
}
/**
* Check if the given property name is valid or not. The property name is considered valid if if is not {@code null}
* and its value is not empty.
* @param propertyValue The value to check.
* @throws java.lang.NullPointerException If the property value is {@code null}.
* @throws java.lang.IllegalArgumentException If the property value is empty.
*/
private static void checkPropertyValue(final String propertyValue) {
if(propertyValue == null) throw new NullPointerException("The property value can not be null");
if(propertyValue.trim().isEmpty()) throw new IllegalArgumentException("The property value can not be empty");
}
/**
* Get the value of a property as a {@link Long}.
* @param propertyName The name of the property to get.
* @return The value of the property or {@code null} if it is not present or can not be parsed.
*/
public static Long getLongProperty(final String propertyName) {
Long value = null;
final String retrievedProperty = getProperty(propertyName);
if(retrievedProperty != null) {
try {
value = Long.parseLong(retrievedProperty);
} catch (NumberFormatException ex) {
LOGGER.log(Level.WARNING, "The value of the property '" + propertyName + "' can not be parsed", ex);
}
}
return value;
}
/**
* Get the value of a property as a {@link Boolean}.
* @param propertyName The name of the property to get.
* @return The value of the property or {@code null} if it is not present or can not be parsed.
*/
public static Boolean getBooleanProperty(final String propertyName) {
Boolean value = null;
final String retrievedProperty = getProperty(propertyName);
if(retrievedProperty != null) {
try {
value = Boolean.parseBoolean(retrievedProperty);
} catch (NumberFormatException ex) {
LOGGER.log(Level.WARNING, "The value of the property '" + propertyName + "' can not be parsed", ex);
}
}
return value;
}
/**
* Check if the auto saving is enabled on exit.
* @return {@code true} if the auto saving is enabled, {@code false} otherwise.
*/
public static boolean isAutoSavingEnabled() {
final Boolean autoSave = getBooleanProperty(AUTO_SAVING_ENABLED_PARAMETER);
return autoSave == null ? Boolean.FALSE : autoSave;
}
/**
* Enable or disable the auto saving configuration..
* @param enabled The value of the parameter.
*/
public static void enableAutoSaving(final boolean enabled) {
setProperty(AUTO_SAVING_ENABLED_PARAMETER, String.valueOf(enabled));
}
/**
* Get the interval for auto saving files.
* @return The interval in minutes.
*/
public static Long getAutoSavingInterval() {
final Long intervalInSeconds = getLongProperty(AUTO_SAVING_INTERVAL_PARAMETER);
return intervalInSeconds == null ? null : TimeUnit.SECONDS.toMinutes(intervalInSeconds);
}
/**
* Set the auto saving interval configuration parameter.
* @param intervalInMinutes The interval in minutes for the auto saving parameter.
*/
public static void setAutoSavingInterval(final long intervalInMinutes) {
setProperty(AUTO_SAVING_INTERVAL_PARAMETER, String.valueOf(TimeUnit.MINUTES.toSeconds(intervalInMinutes)));
}
/**
* Removes the auto saving interval from the configuration.
*/
public static void removeAutoSavingInterval() {
removeProperty(AUTO_SAVING_INTERVAL_PARAMETER);
}
/**
* Check if the temporary files deletion is enabled on exit.
* @return {@code true} if the deletion is enabled, {@code false} otherwise.
*/
public static boolean isTemporaryFilesDeletionOnExitEnabled() {
final Boolean deleteTemporaryFiles = getBooleanProperty(TEMPORARY_FILES_DELETION_ON_EXIT_PARAMETER);
return deleteTemporaryFiles == null ? false : deleteTemporaryFiles;
}
/**
* Sets the default log level of the application.
* @param level The desired log level.
*/
public static void setLogLevel(final Level level) {
setProperty(LOG_LEVEL_PARAMETER, level.getName());
}
/**
* Sets the default log handlers.
* @param handlers The handlers of logs.
*/
public static void setLogHandler(final Class<? extends Handler> ... handlers) {
final StringJoiner joiner = new StringJoiner(" ");
Arrays.stream(handlers).forEach(handler -> joiner.add(handler.getName()));
setProperty(LOG_HANDLERS_PARAMETER, joiner.toString());
}
/**
* Sets the encoding of log files.
* @param handler The class handler to set the encoding for.
* @param charset The encoding of log files.
*/
public static void setLogEncoding(final Class<? extends Handler> handler, final Charset charset) {
setProperty(handler.getName().concat(LOG_ENCODING_SUFFIX), charset.displayName());
}
/**
* Sets the size in bytes of the log files.
* @param size The size, in bytes, of log files.
*/
public static void setLogFileLimit(final long size) {
setProperty(LOG_FILE_LIMIT_PARAMETER, String.valueOf(size));
}
/**
* Sets the log files pattern.
* @param pattern The pattern of log files.
*/
public static void setLogFilePattern(final String pattern) {
setProperty(LOG_FILE_PATTERN_PARAMETER, pattern);
}
/**
* Sets the class responsible of formatting log files.
* @param handler The class handler to set the formatter for.
* @param formatter The formatter to use for log files.
*/
public static void setLogFormatter(final Class<? extends Handler> handler, final Class<? extends Formatter> formatter) {
setProperty(handler.getName().concat(LOG_FORMATTER_SUFFIX), formatter.getName());
}
/**
* Defines if the logs should be append or not to the log files.
* @param append {@code true} to allow appending, {@code false} otherwise.
*/
public static void setLogFileAppend(final boolean append) {
setProperty(LOG_FILE_APPEND_PARAMETER, String.valueOf(append));
}
/**
* Enable or disable the temporary files deletion.
* @param enable {@code true} to enable the deletion, {@code false} otherwise.
*/
public static void enableTemporaryFilesDeletionOnExit(final boolean enable) {
setProperty(TEMPORARY_FILES_DELETION_ON_EXIT_PARAMETER, String.valueOf(enable));
}
/**
* Get the temporary files max age parameter's value.
* @return The max age of temporary files in days.
*/
public static Long getTemporaryFilesMaxAge() {
final Long ageInSeconds = getLongProperty(TEMPORARY_FILES_MAX_AGE_PARAMETER);
return ageInSeconds == null ? null : TimeUnit.SECONDS.toDays(ageInSeconds);
}
/**
* Set the max age of temporary files before they are deleted.
* @param maxAgeInDays The max age of the temporary files.
*/
public static void setTemporaryFilesMaxAge(final long maxAgeInDays) {
setProperty(TEMPORARY_FILES_MAX_AGE_PARAMETER, String.valueOf(TimeUnit.DAYS.toSeconds(maxAgeInDays)));
}
/**
* Remove the temporary files max age from the configuration.
*/
public static void removeTemporaryFilesMaxAge() {
removeProperty(TEMPORARY_FILES_MAX_AGE_PARAMETER);
}
/**
* Get the default {@link Charset} used by the application.
* @return The default charset used by the application.
*/
public static Charset getDefaultCharset() { return DEFAULT_CHARSET; }
}