Image of How to implement an auto reloadable XML using Apache VFS

ADVERTISEMENT

Table of Contents

Introduction

XML is usually used to store light configuration that controls the business flow or the layout of an application, it’s meant to be managed by business users in the production phase due to its simple human-readable syntax.

A typical example is to store the default theme/layout of the application, control the accepted currencies of a financial application, turning on/off features of an application.

Since these configurations are prone to regular updates, it’s not practical to restart the application on each configuration change.

In this tutorial, we describe how to implement an auto reloadable XML using Apache VFS in Java.

1- app.xml

Suppose we have the following app.xml file which holds the business and layout configuration for our financial application:

<appConfiguration>
    <defaultTheme>dark</defaultTheme>
    <currencies>
        <currency>USD</currency>
        <currency>EURO</currency>
    </currencies>
    <features>
        <transferBalance>true</transferBalance>
    </features>
</appConfiguration>

2- pom.xml

In order to use Apache VFS, we add the following dependencies to pom.xml:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-vfs2</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>1.6</version>
</dependency>

3- AppConfiguration.java

Create a POJO class named as AppConfiguration.java which maps the attributes of app.xml:

public class AppConfiguration {
 
    private String defaultTheme;
    private List<String> currencies;
    private boolean isTransferBalance;
        
        // Getters, Setters 
}

4- XMLConfigurationManager.java

Now, this is our configuration processor, we’ll create a class which loads the configuration at the application startup and listens for any runtime change on the XML file.

Our manager mainly acts as a cache and may be defined as a Singleton which populates AppConfiguration bean in the constructor and repopulates it on each XML change. Whenever we need to read the configuration from other classes or modules, we get an instance of the Cache and use AppConfiguration, hence any XML change will be reflected instantly on the application without the need for a restart.

Below is the implementation of XMLConfigurationManager:

public class XMLConfigurationManager {
    
    private AppConfiguration appConfigurationBean;
    private File xmlFile;
 
    public XMLConfigurationManager(){
        try
        {
            xmlFile = ResourceUtils.getFile("classpath:app.xml");
            loadConfiguration();
            fileChangedListener(xmlFile);
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
 
    private void loadConfiguration()
    {
        try
        {
            appConfigurationBean = new AppConfiguration();
            XMLConfiguration xmlConfiguration = new XMLConfiguration(xmlFile);
            
            String defaultTheme = xmlConfiguration.getString("defaultTheme");
            Boolean isTransferBalance = Boolean.valueOf(xmlConfiguration.getString("features.transferBalance"));
            List<String> currencies = new ArrayList<String>();
            List<HierarchicalConfiguration> currenciesConfig = xmlConfiguration.configurationsAt("currencies.currency");
            for(HierarchicalConfiguration currencyConfig: currenciesConfig)
            {
                currencies.add(currencyConfig.getString(""));
            }
            
            appConfigurationBean.setDefaultTheme(defaultTheme);
            appConfigurationBean.setCurrencies(currencies);
            appConfigurationBean.setTransferBalance(isTransferBalance);
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
 
    /**
     * This method is called to send a listener on the file being modified or
     * changed.
     * 
     * @param file
     * @throws FileSystemException
     */
    private void fileChangedListener(File file) throws FileSystemException {
 
        FileSystemManager fsManager = VFS.getManager();
        FileObject listendir = fsManager.resolveFile(file.getAbsolutePath());
 
        DefaultFileMonitor fm = new DefaultFileMonitor(new FileListener() {
 
            @Override
            public void fileChanged(FileChangeEvent arg0) throws Exception {
                System.out.println("File Change event ");
                loadConfiguration();
            }
 
            @Override
            public void fileCreated(FileChangeEvent arg0) throws Exception {
                System.out.println("File Created event ");
                loadConfiguration();
            }
 
            @Override
            public void fileDeleted(FileChangeEvent arg0) throws Exception {
                System.out.println("File Delete event ");
                loadConfiguration();
            }
        });
        fm.setRecursive(true);
        fm.addFile(listendir);
        fm.start();
    }
    
    public AppConfiguration getAppConfigurationBean() {
        return appConfigurationBean;
    }
}

As we notice, we read the configuration in the constructor and attach an event listener to the XML file using DefaultFileMonitor and FileListener classes provided by Apache VFS. On each change event, we call loadConfiguration() to repopulate the AppConfiguration bean.

Through this way, the latest version of XML would always be exposed to the application through XMLConfigurationManager and AppConfiguration bean.

Summary

In this tutorial, we describe how to implement an auto reloadable XML using Apache VFS in Java.

Next Steps

If you're interested in learning more about the basics of Java, coding, and software development, check out our Coding Essentials Guidebook for Developers, where we cover the essential languages, concepts, and tools that you'll need to become a professional developer.

Thanks and happy coding! We hope you enjoyed this article. If you have any questions or comments, feel free to reach out to jacob@initialcommit.io.

Final Notes