The Configuration Utils API is defined by the IConfigurations interface.
All configuration functionality is defined by this interface and all implementations must implement it. For a list of all the features this interface defines refer to it's JavaDoc documentation.
The BaseConfigurationsImpl is a standard base implementation of the Configuration Utils API as specified by the IConfigurations interface. The goal of this class is to facilitate the development of new Implementation classes of the Configuration Utils API by providing default implementations of most of it's methods. These implementations represent the tasks that are normally handled the same way in any Configuration repository we may want to implement, like the parsing of POJOs, type conversion, default values, etc.
This class is provided as a helper in the implementation of a new Configuration Utils Implementation. Using the BaseConfigurationsImpl as a starting point to our Implementation one no longer needs to implement all methods of the API IConfigurations, since BaseConfigurationsImpl takes care of the large majority of them for us.
All that's left for us to do is:
For a sample implementation let's create an implementation that simply writes messages to a fixed "configurations.properties". This is of course a bad solution since this poor file will grow immensely with the addition of more and more configurations. This is only so we don't over-complicate our example.
Good implementations could for instance be:
We will use BaseConfigurationsImpl as a starting point for our implementation since it will save us the trouble of implementing all the helper methods and standard features like, for instance, POJO parsing.
The newly created class that extends BaseConfigurationsImpl:
1 public class ConfigurationsPropertyFileImpl extends BaseConfigurationsImpl { 2 }
This of course is a class with a handful of compilation errors. We must implement several methods before it is ready. Let's continue.
Let's give our class a constructor and a private attribute for the repository.
1 public class ConfigurationsPropertyFileImpl extends BaseConfigurationsImpl { 2 3 /** the filePath of the repository on disk */ 4 private String repositoryFilePath; 5 6 /** The properties object in memory, our memory representation of the repository */ 7 private Properties repository; 8 9 /** 10 * Default constructor 11 */ 12 public ConfigurationsPropertyFileImpl() { 13 14 // Set the file path based on the current user home directory 15 repositoryFilePath = System.getProperty("user.dir") + "\\configurations.properties"; 16 17 // Initialize the repository 18 repository = new Properties(); 19 20 try { 21 repository.load(new FileInputStream(repositoryFilePath)); 22 } catch (FileNotFoundException e) { 23 System.out.print("Repository file does not exist. Starting a new repository from scratch"); 24 25 } catch (IOException e) { 26 System.out.print("Error reading repository. Using a new, empty one."); 27 } 28 } 29 }
Our constructor initializes our repository object and loads all existing configurations from disk.
We must next implement three methods. These three methods are the only ones BaseConfigurationsImpl could not provide for us.
For all of these we'll have to implement a little workaround for the fact that properties files only allow a simple key to organize values. There are no sections in the files. So if we want to organize keys by section and allow equal keys to exists in different sections, we will have to use a compiled key with the configID, sectionID and the key itself. All three methods implement this workaround.
repositoryKey = configID + "." + sectionID + "." + key
Since this will be needed more than once, lets provide an utility that saves the repository in memory to the disk.
30 /** 31 * @return T if the repository was successfully saved 32 */ 33 private boolean saveRepository() { 34 try { 35 // Update the file on disk 36 repository.store(new FileOutputStream(repositoryFilePath), ""); 37 38 } catch (FileNotFoundException e) { 39 System.out.print("Error writing repository. Changes will not persist."); 40 return false; 41 42 } catch (IOException e) { 43 System.out.print("Error writing repository. Changes will not persist."); 44 return false; 45 } 46 47 return true; 48 }
A simple implementation that searches the repository for keys that contain the required prefix.
50 /** 51 * @see pt.digitalis.utils.config.BaseConfigurationsImpl#readConfiguration(java.lang.String, java.lang.String) 52 */ 53 @Override 54 public Properties readConfiguration(String configID, String sectionID) { 55 Properties props = new Properties(); 56 57 String prefix = configID + "." + sectionID + "."; 58 59 // Reads every key that starts with "configID.sectionID." and adds it without this prefix 60 for (Object key : repository.keySet()) 61 if (((String) key).startsWith(prefix)) 62 props.put(((String) key).substring(prefix.length()), repository.get(key)); 63 64 return props; 65 }
Adds a set of key/values to the current repository and persists it to disk.
67 /** 68 * @see pt.digitalis.utils.config.BaseConfigurationsImpl#writeConfiguration(java.lang.String, java.lang.String, 69 * java.util.Properties) 70 */ 71 @Override 72 public boolean writeConfiguration(String configID, String sectionID, Properties values) { 73 74 String prefix = configID + "." + sectionID + "."; 75 76 // Adds all key/values adding the prefix "configID.sectionID." to each key 77 for (Object key : values.keySet()) 78 repository.put(prefix + key, values.get(key)); 79 80 return saveRepository(); 81 }
Removes all key/value for the corresponding configID/sectionID from the repository and persists it.
83 /** 84 * @see pt.digitalis.utils.config.IConfigurations#removeConfiguration(java.lang.String, java.lang.String) 85 */ 86 public boolean removeConfiguration(String configID, String sectionID) { 87 Properties props = new Properties(); 88 89 String prefix = configID + "." + sectionID + "."; 90 91 // Remove every key that starts with "configID.sectionID." 92 for (Object key : repository.keySet()) 93 if (((String) key).startsWith(prefix)) 94 props.remove(key); 95 96 return saveRepository(); 97 }
And this ends our implementation. Of course this is not a very intelligent implementation. The performance will be exponential, since it writes the full repository for each write/remove operation. The repository fully in memory may also become a problem. Still, this is only a sample implementation in no way intended to be a valid alternative to the default implementation provided by Configuration Utils.
You can see how easy it is create your own Configuration implementation, if you need to.
For a better view we will present the complete listing of our example implementation bellow. If you would like to play with the code, you can download the java source file here.
1 public class ConfigurationsPropertyFileImpl extends BaseConfigurationsImpl { 2 3 /** the filePath of the repository on disk */ 4 private String repositoryFilePath; 5 6 /** The properties object in memory, our memory representation of the repository */ 7 private Properties repository; 8 9 /** 10 * Default constructor 11 */ 12 public ConfigurationsPropertyFileImpl() { 13 14 // Set the file path based on the current user home directory 15 repositoryFilePath = System.getProperty("user.dir") + "\\configurations.properties"; 16 17 // Initialize the repository 18 repository = new Properties(); 19 20 try { 21 repository.load(new FileInputStream(repositoryFilePath)); 22 } catch (FileNotFoundException e) { 23 System.out.print("Repository file does not exist. Starting a new repository from scratch"); 24 25 } catch (IOException e) { 26 System.out.print("Error reading repository. Using a new, empty one."); 27 } 28 } 29 30 /** 31 * @return T if the repository was successfully saved 32 */ 33 private boolean saveRepository() { 34 try { 35 // Update the file on disk 36 repository.store(new FileOutputStream(repositoryFilePath), ""); 37 38 } catch (FileNotFoundException e) { 39 System.out.print("Error writing repository. Changes will not persist."); 40 return false; 41 42 } catch (IOException e) { 43 System.out.print("Error writing repository. Changes will not persist."); 44 return false; 45 } 46 47 return true; 48 } 49 50 /** 51 * @see pt.digitalis.utils.config.BaseConfigurationsImpl#readConfiguration(java.lang.String, java.lang.String) 52 */ 53 @Override 54 public Properties readConfiguration(String configID, String sectionID) { 55 Properties props = new Properties(); 56 57 String prefix = configID + "." + sectionID + "."; 58 59 // Reads every key that starts with "configID.sectionID." and adds it without this prefix 60 for (Object key : repository.keySet()) 61 if (((String) key).startsWith(prefix)) 62 props.put(((String) key).substring(prefix.length()), repository.get(key)); 63 64 return props; 65 } 66 67 /** 68 * @see pt.digitalis.utils.config.BaseConfigurationsImpl#writeConfiguration(java.lang.String, java.lang.String, 69 * java.util.Properties) 70 */ 71 @Override 72 public boolean writeConfiguration(String configID, String sectionID, Properties values) { 73 74 String prefix = configID + "." + sectionID + "."; 75 76 // Adds all key/values adding the prefix "configID.sectionID." to each key 77 for (Object key : values.keySet()) 78 repository.put(prefix + key, values.get(key)); 79 80 return saveRepository(); 81 } 82 83 /** 84 * @see pt.digitalis.utils.config.IConfigurations#removeConfiguration(java.lang.String, java.lang.String) 85 */ 86 public boolean removeConfiguration(String configID, String sectionID) { 87 Properties props = new Properties(); 88 89 String prefix = configID + "." + sectionID + "."; 90 91 // Remove every key that starts with "configID.sectionID." 92 for (Object key : repository.keySet()) 93 if (((String) key).startsWith(prefix)) 94 props.remove(key); 95 96 return saveRepository(); 97 } 98 }