Skip to content

Commit

Permalink
Configuration is only initialized once (#9)
Browse files Browse the repository at this point in the history
* Hot load configuration file

- Detect configuration file modification
- Failsafe new configuration loading, old one will remain if issue occurs in new one
- Application stops if initial configuration loading fails

* Unit test and improvements

- Add unit tests for configuration loading
- Improve code lisibility
- Fix small bugs
  • Loading branch information
Ramzay authored Jul 20, 2020
1 parent c761ed2 commit 0927b01
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 50 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ build/
### VS Code ###
.vscode/
**script**
*config.json
*config.json

### Logs ###
*.log
17 changes: 16 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.smart.home</groupId>
<artifactId>file.is.my.command</artifactId>
<version>1.1-SNAPSHOT</version>
<version>1.2-SNAPSHOT</version>
<name>pc.daemon</name>
<packaging>jar</packaging>
<description>Daemon that will be installed on computers and will handle simple daily tasks</description>
Expand Down Expand Up @@ -82,5 +82,20 @@
<version>3.4.2</version>
</dependency>


<!-- TEST SCOPE -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.23.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

</dependencies>
</project>
78 changes: 30 additions & 48 deletions src/main/java/com/smart/home/pc/daemon/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.gson.Gson;
import com.smart.home.pc.daemon.dto.Config;
import com.smart.home.pc.daemon.dto.FullConfiguration;
import com.smart.home.pc.daemon.impl.Daemon;
import com.smart.home.pc.daemon.service.impl.ConfigurationLoaderServiceImpl;

public class Application {

Expand All @@ -30,57 +28,24 @@ public static void main(String[] args) throws RuntimeException, IOException, Int
// Determine local jar path
Path jarAbsolutePath = FileSystems.getDefault().getPath("").toAbsolutePath();

// Check and create configuration file if not found
String configPath = jarAbsolutePath.toString() + File.separator + CONFIGURATION_FILE_NAME;
Path configFilePath = Paths.get(configPath);
File configFile = new File(configPath);

// Create configuration file
if (!configFile.isFile()) {
try {
configFile.createNewFile();
} catch (IOException e) {
LOGGER.error("Could not create the configuration file: " + configFilePath.toString());
e.printStackTrace();
System.exit(1);
}
}

if (configFile.isFile()) {
LOGGER.info("Configuration location: " + configPath);
}

// create Gson instance
Gson gson = new Gson();
Config daemonConfig = new Config();

// create a reader
Reader reader;
try {
reader = Files.newBufferedReader(configFilePath);

// convert JSON file to map
daemonConfig = gson.fromJson(reader, Config.class);
reader.close();

} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// Load configuration file
String initialUserConfigurationPath = jarAbsolutePath.toString() + File.separator + CONFIGURATION_FILE_NAME;
ConfigurationLoaderServiceImpl configurationLoaderService = new ConfigurationLoaderServiceImpl(initialUserConfigurationPath);
Config initialUserConfiguration = configurationLoaderService.getCurrentUserConfiguration();

// Configuration not found we exit the app for now
if (daemonConfig == null) {
if (initialUserConfiguration == null) {
LOGGER.error("Configuration file is either empty or malformed, exiting...");
System.exit(1);
}

// Create script directory
if (StringUtils.isBlank(daemonConfig.getScriptDir())) {
if (StringUtils.isBlank(initialUserConfiguration.getScriptDir())) {
LOGGER.warn("Missing scriptDir property in configuration file. Will be using 'script' dir");
daemonConfig.setScriptDir("script");
initialUserConfiguration.setScriptDir("script");
}
Path batchPath = Paths.get(jarAbsolutePath + File.separator + daemonConfig.getScriptDir() + File.separator);

Path batchPath = Paths.get(jarAbsolutePath + File.separator + initialUserConfiguration.getScriptDir() + File.separator);
LOGGER.info("Script location:" + batchPath);

try {
Expand All @@ -93,11 +58,28 @@ public static void main(String[] args) throws RuntimeException, IOException, Int

// Initialise and run Daemon
FullConfiguration configuration = new FullConfiguration();
configuration.setUserConfiguration(daemonConfig);
configuration.setUserConfiguration(initialUserConfiguration);
configuration.setAbsPathHomeFolder(jarAbsolutePath.toString());
configuration.setAbsPathScriptFolder(configuration.getAbsPathHomeFolder() + File.separator + daemonConfig.getScriptDir() + File.separator);
configuration.setAbsPathScriptFolder(configuration.getAbsPathHomeFolder() + File.separator + initialUserConfiguration.getScriptDir() + File.separator);

Daemon deamon = new Daemon();
while (true) {

// New configuration detected
if (configurationLoaderService.isConfigurationUpdated(initialUserConfigurationPath)) {

if (configuration.getUserConfiguration().isLogEverything()) {
LOGGER.info("New configuration detected !");
}
// Load configuration
Config newUserConfiguration = configurationLoaderService.loadConfiguration(initialUserConfigurationPath);
if (newUserConfiguration != null) {
configuration.setUserConfiguration(newUserConfiguration);
} else {
LOGGER.warn("Cound not load new configuration, will keep using the old one !");
}
}

deamon.run(configuration);
Thread.sleep(10000);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.smart.home.pc.daemon.service;

import com.smart.home.pc.daemon.dto.Config;

public interface ConfigurationLoaderService {

public Config loadConfiguration(String configurationPath);

public boolean isConfigurationUpdated(String configurationPath);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.smart.home.pc.daemon.service.impl;

import java.util.Date;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.smart.home.pc.daemon.dto.Config;
import com.smart.home.pc.daemon.service.ConfigurationLoaderService;
import com.smart.home.pc.daemon.util.FileHelper;

public class ConfigurationLoaderServiceImpl implements ConfigurationLoaderService {

private static final Logger LOGGER = LogManager.getLogger(ConfigurationLoaderServiceImpl.class);

private Date configurationFileLastModifiedDate;

private Config currentUserConfiguration;

private FileHelper fileHelper = new FileHelper();

public ConfigurationLoaderServiceImpl() {

}

public ConfigurationLoaderServiceImpl(String configurationPath) {
loadConfiguration(configurationPath);
}

@Override
public Config loadConfiguration(String configurationPath) {
Date configurationPathLastModificationDate = fileHelper.getFileLastModificationDate(configurationPath);
// Configuration is found and is a file.
if (configurationPathLastModificationDate != null) {
Config loadedConfiguration = fileHelper.getConfigurationFromPath(configurationPath);
if (loadedConfiguration != null) {
configurationFileLastModifiedDate = configurationPathLastModificationDate;
currentUserConfiguration = loadedConfiguration;
return currentUserConfiguration;
} else {
LOGGER.error("Issue loading configuration file: " + configurationPath);
}
} else {
// Configuration file not found or null
LOGGER.error("The provided configuration path was not found: " + configurationPath);
}
return null;
}

@Override
public boolean isConfigurationUpdated(String configurationPath) {
if (configurationPath != null) {
Date currentConfigDate = fileHelper.getFileLastModificationDate(configurationPath);
if (currentConfigDate == null || currentConfigDate.equals(configurationFileLastModifiedDate)) {
return false;
}
}
return true;
}

public Date getConfigurationLoadDate() {
return configurationFileLastModifiedDate;
}

public void setConfigurationLoadDate(Date configurationLoadDate) {
this.configurationFileLastModifiedDate = configurationLoadDate;
}

public Config getCurrentUserConfiguration() {
return currentUserConfiguration;
}

public void setCurrentUserConfiguration(Config currentUserConfiguration) {
this.currentUserConfiguration = currentUserConfiguration;
}

}
79 changes: 79 additions & 0 deletions src/main/java/com/smart/home/pc/daemon/util/FileHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.smart.home.pc.daemon.util;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.gson.Gson;
import com.smart.home.pc.daemon.dto.Config;

/**
* Helper class to manage file actions
*
* @author Ramzi
*
*/
public class FileHelper {

private static final Logger LOGGER = LogManager.getLogger(FileHelper.class);

private Gson gson = new Gson();

/**
* Get last modification date for the given file path
*
* @param filePath, String containing the absolute path of the file
* @return last modification date or null
*/
public Date getFileLastModificationDate(String fileAbsPath) {
if (StringUtils.isEmpty(fileAbsPath)) {
return null;
}

File configFile = new File(fileAbsPath);
if (configFile.exists()) {
return new Date(configFile.lastModified());
}

return null;
}

/**
* Reads the configuration file as a Json and returns the corresponding object
*
* @param configFilePath
* @return
*/
public Config getConfigurationFromPath(String configurationPath) {
Path configFilePath = Paths.get(configurationPath);
Config userConfiguration = null;

Reader reader;
try {
reader = Files.newBufferedReader(configFilePath);

// convert JSON file to map
userConfiguration = gson.fromJson(reader, Config.class);
reader.close();

} catch (Exception e) {
e.printStackTrace();
LOGGER.error("Exception while reading configuration file please check file format: " + configFilePath);
}

if (userConfiguration == null) {
LOGGER.error("Configuration file is either empty or malformed");
}

return userConfiguration;
}

}
Loading

0 comments on commit 0927b01

Please sign in to comment.