views:

96

answers:

2

I want to build a ApplicationSetting for my application. The ApplicationSetting can be stored in a properties file or in a database table. The settings are stored in key-value pairs. E.g.

ftp.host = blade
ftp.username = dummy
ftp.pass = pass
content.row_pagination = 20
content.title = How to train your dragon.

I have designed it as follows:

Application settings reader:

    interface IApplicationSettingReader {
    Map read();
}

DatabaseApplicationSettingReader implements IApplicationSettingReader {
    dao appSettingDao;

    Map read() {
        List<AppSettingEntity> listEntity = appSettingsDao.findAll();
        Map<String, String> map = new HaspMap<String, String>();
        foreach (AppSettingEntity entity : listEntity) {
            map.put(entity.getConfigName(), entity.getConfigValue());
        }

        return new AppSettings(map);
    }
}

DatabaseApplicationSettingReader implements IApplicationSettingReader {
    dao appSettingDao;

    Map read() {
        //read from some properties file 
        return new AppSettings(map);
    }
}

Application settings class:

AppSettings {
private static AppSettings instance = new AppSettings();
private Map map;

private AppSettings() {
}



public static AppSettings getInstance() {
    if (instance == null) {
        throw new RuntimeException("Object not configure yet");
    } 

    return instance;
}

public static configure(IApplicationSettingReader reader) {
    this.map = reader.read();

}

public String getFtpSetting(String param) {
    return map.get("ftp." + param);
}

public String getContentSetting(String param) {
    return map.get("content." + param);
}

}

Test class:

AppSettingsTest {
    IApplicationSettingReader reader;

    @Before
    public void setUp() throws Exception {
        reader = new DatabaseApplicationSettingReader();
    }

    @Test
    public void  getContentSetting_should_get_content_title() {
        AppSettings.configure(reader);

        Instance settings = AppSettings.getInstance();
        String title = settings.getContentSetting("title");
        assertNotNull(title);
        Sysout(title);
    }

}

My questions are:

  1. Can you give your opinion about my code, is there something wrong ?????

  2. I configure my application setting once, while the application start, I configure the application setting with appropriate reader (DbReader or PropertiesReader), I make it singleton because the application just have one instance of ApplicationSettngs. The problem is, when some user edit the database or file directly to database or file, I can't get the changed values. Now, I want to implement something like ApplicationSettingChangeListener. So if the data changes, I will refresh my application settings. Do you have any suggestions how this can be implementedb ????

A: 

I haven't throughly inspected your code, but there seems to be a concurrency issue. The map is thread-unsafe (HashMap), so if you mutate it through config() and have other threads access map, you have a problem.

Though you could use a ConcurrentHashMap instead HashMap, a batch operation on ConcurrentHashMap is not atomic. Meaning that, if you use it, you will see a "half-way" modified config. That could not be okay depending on your app.

So, the solution for this is to use this:

private volatile ImmutableMap map;

public config(){
    ImmutableMap newMap = createNewMap();
    this.map = newMap;
}

This will change your configs atomically (no intermediate state is visible).

As for updating your config on the fly, log4j does it using a background thread that monitors the config file. You could of course monitor a db table instead by polling it periodically.

In that case, your Config class will have preferably a ScheduledExecutor with a task that will monitor files/db and call config() periodically.

Enno Shioji
A: 

The answer to question #2 is to use a thread and check periodically if the file has been changed or to simply reinitialize your settings with the file contents.

Jan Kuboschek