-1

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();
        }
    }

}
AndreFontaine
  • 2,334
  • 3
  • 17
  • 30

1 Answers1

1

You can take a look at MBeans.

An MBean is a managed Java object, similar to a JavaBeans component, that follows the design patterns set forth in the JMX specification. An MBean can represent a device, an application, or any resource that needs to be managed.

Because you are using Spring you can do something like this:

@Component
@ManagedResource(objectName = com.whatever:name=Configurator",
    description = "Whatever JMX Configurator")
public class WhateverJMXBean {

    private final Configurator configurator;

    public WhateverJMXBean(final Configurator configurator){
        this.configurator = configurator;
    }

    @ManagedAttribute
    public void setField(String field) {
        configurator.setField(field);
    }

    @ManagedAttribute
    public String getField() {
        return configurator.getField();
    }
}

And the Configurator class is the handler of your properties, basically a POJO:

@Data
@Configuration
public class Configurator{
    private String field;

    public void setField(String field){
        this.field = field;
    }

    public String getField(){
        return this.field;
    }
}

You will be able to set and interrogate the properties from the JConsole.

LoolKovsky
  • 1,018
  • 1
  • 12
  • 29