I already achieved how to update external properties at run time but I think it can be improved and I'm not sure about possible errors than I can be missing.
First I'm setting to the vm the external location as -Dspring.config.location
in that file a have an entry as json.properties.location = /project/properties/data.json
. Then into a service a call with value that location:
@Service
public class DataService {
@Value("${json.properties.location}")
private String purposeFile;
static List<WireTransferPurpose> purposeList = new HashMap<>();
private static void generateData(String filePath) {
// Initialize the purpose list
if (purposeList.isEmpty()) {
// create the path with the file location
Path path = Paths.get(filePath);
StringBuilder data = new StringBuilder();
try(Stream<String> lines = Files.lines(path)) {
lines.forEach(line -> data.append(line).append("\n"));
}
catch (Exception e) {
throw new PropertiesFilesNotFoundException("Error reading the properties file.");
}
// maps JSON file content to the indicated object
Type mapType = new TypeToken<List<WireTransferPurpose>>() {
private static final long serialVersionUID = -2457565451021504055L;
}.getType();
try {
purposeList = new Gson().fromJson(data.toString().trim(), mapType);
}
catch (Exception e) {
throw new NotValidPorpertiesDataException();
}
}
}
public static void refreshProperties() {
purposeList.clear();
}
private List<WireTransferPurpose> purposeList( {
generateData(purposeFile);
return purposeList;
}
}
Here I read the file and create and object with it's content, if the Object List is already created do nothing and there's also a refresh method that just call the clear method to the List.
Then I have the watcher component:
@Configuration
public class PropertiesWatcher {
private static final String PROPERTIES_LOCATION = "\\project\\properties";
public static void refreshService() throws IOException, InterruptedException {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get(PROPERTIES_LOCATION);
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
DataService.refreshProperties();
}
}
key.reset();
}
}
}
The last step it's to run the service somewhere and here is where I have more doubts, I placed in the main spring boot application class, principally for the use of static methods:
@SpringBootApplication
public class WireTransferLimitApplication {
public static void main(String[] args) {
SpringApplication.run(WireTransferLimitApplication.class, args);
propertiesWatcher();
}
/**
* Properties watcher.
*/
private static void propertiesWatcher() {
try {
PropertiesWatcher.refreshService();
}
catch (IOException | InterruptedException e) {
// Restore interrupted state
Thread.currentThread().interrupt();
}
}
}