diff --git a/libresonic-main/pom.xml b/libresonic-main/pom.xml
index 3f820085..499b9ffc 100644
--- a/libresonic-main/pom.xml
+++ b/libresonic-main/pom.xml
@@ -353,6 +353,19 @@
+
+ org.apache.commons
+ commons-configuration2
+ 2.1
+
+
+ commons-beanutils
+ commons-beanutils
+ 1.9.2
+
+ runtime
+
+
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/ApacheCommonsConfigurationService.java b/libresonic-main/src/main/java/org/libresonic/player/service/ApacheCommonsConfigurationService.java
new file mode 100644
index 00000000..dc8e0f6e
--- /dev/null
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/ApacheCommonsConfigurationService.java
@@ -0,0 +1,103 @@
+package org.libresonic.player.service;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.FileBasedConfiguration;
+import org.apache.commons.configuration2.ImmutableConfiguration;
+import org.apache.commons.configuration2.MapConfiguration;
+import org.apache.commons.configuration2.PropertiesConfiguration;
+import org.apache.commons.configuration2.PropertiesConfigurationLayout;
+import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
+import org.apache.commons.configuration2.builder.fluent.Parameters;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.configuration2.sync.ReadWriteSynchronizer;
+import org.apache.commons.io.FileUtils;
+import org.libresonic.player.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class ApacheCommonsConfigurationService {
+
+ private static final Logger LOG = Logger.getLogger(ApacheCommonsConfigurationService.class);
+
+ private final FileBasedConfigurationBuilder builder;
+
+ private final Configuration config;
+
+ public static final String HEADER_COMMENT = "Libresonic preferences. NOTE: This file is automatically generated."
+ + " Do not modify while application is running";
+
+ public ApacheCommonsConfigurationService() {
+ File propertyFile = SettingsService.getPropertyFile();
+ if(!propertyFile.exists()) {
+ try {
+ FileUtils.touch(propertyFile);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not create new property file", e);
+ }
+ }
+ Parameters params = new Parameters();
+ PropertiesConfigurationLayout layout = new PropertiesConfigurationLayout();
+ // https://issues.apache.org/jira/browse/CONFIGURATION-644
+// layout.setHeaderComment(HEADER_COMMENT);
+ layout.setGlobalSeparator("=");
+ builder = new FileBasedConfigurationBuilder(PropertiesConfiguration.class).configure(
+ params.properties()
+ .setFile(propertyFile)
+ .setSynchronizer(new ReadWriteSynchronizer())
+ .setLayout(layout));
+ try {
+ config = builder.getConfiguration();
+ } catch (ConfigurationException e) {
+ throw new RuntimeException("Could not load property file at " + propertyFile, e);
+ }
+ }
+
+ public void save() {
+ try {
+ builder.save();
+ } catch (ConfigurationException e) {
+ LOG.error("Unable to write to property file.", e);
+ }
+ }
+
+ public Object getProperty(String key) {
+ return config.getProperty(key);
+ }
+
+ public boolean containsKey(String key) {
+ return config.containsKey(key);
+ }
+
+ public void clearProperty(String key) {
+ config.clearProperty(key);
+ }
+
+ public String getString(String key, String defaultValue) {
+ return config.getString(key, defaultValue);
+ }
+
+ public void setProperty(String key, Object value) {
+ config.setProperty(key, value);
+ }
+
+ public long getLong(String key, long defaultValue) {
+ return config.getLong(key, defaultValue);
+ }
+
+ public int getInteger(String key, int defaultValue) {
+ return config.getInteger(key, defaultValue);
+ }
+
+ public boolean getBoolean(String key, boolean defaultValue) {
+ return config.getBoolean(key, defaultValue);
+ }
+
+ public ImmutableConfiguration getImmutableSnapshot() {
+ MapConfiguration mapConfiguration = new MapConfiguration(new HashMap<>());
+ mapConfiguration.copy(config);
+ return mapConfiguration;
+ }
+}
diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java b/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
index 1f7185b7..25c8e534 100644
--- a/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
+++ b/libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
@@ -19,29 +19,6 @@
*/
package org.libresonic.player.service;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.libresonic.player.Logger;
@@ -61,6 +38,12 @@ import org.libresonic.player.util.FileUtil;
import org.libresonic.player.util.StringUtil;
import org.libresonic.player.util.Util;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.concurrent.*;
+
/**
* Provides persistent storage of application settings and preferences.
*
@@ -219,13 +202,13 @@ public class SettingsService {
private static final Logger LOG = Logger.getLogger(SettingsService.class);
- private Properties properties = new Properties();
private List themes;
private List locales;
private InternetRadioDao internetRadioDao;
private MusicFolderDao musicFolderDao;
private UserDao userDao;
private AvatarDao avatarDao;
+ private ApacheCommonsConfigurationService configurationService;
private VersionService versionService;
private String[] cachedCoverArtFileTypesArray;
@@ -234,34 +217,33 @@ public class SettingsService {
private List cachedMusicFolders;
private final ConcurrentMap> cachedMusicFoldersPerUser = new ConcurrentHashMap>();
- private static File libresonicHome;
-
- private static final long LOCAL_IP_LOOKUP_DELAY_SECONDS = 60;
private String localIpAddress;
- public SettingsService() {
- File propertyFile = getPropertyFile();
+ private void removeObseleteProperties() {
- if (propertyFile.exists()) {
- FileInputStream in = null;
- try {
- in = new FileInputStream(propertyFile);
- properties.load(in);
- } catch (Exception x) {
- LOG.error("Unable to read from property file.", x);
- } finally {
- IOUtils.closeQuietly(in);
+ OBSOLETE_KEYS.forEach( oKey -> {
+ if(configurationService.containsKey(oKey)) {
+ LOG.info("Removing obsolete property [" + oKey + ']');
+ configurationService.clearProperty(oKey);
}
+ });
- // Remove obsolete properties.
- for (Iterator