It has been almost five weeks since work started on the 52° North Web Processing Service (WPS) web admin project. So far, the work has focused entirely on the back-end where we aim to create a new and more robust configuration manager. The project currently uses an XML approach in which configurations are stored in an XML file, loaded and read manually, module by module and property by property. The WPS has three main types of configuration modules: Processes, Generators, and Parsers. To load the generators, for example, you first have to parse the XML file, then get the generators list and then read properties and formats one by one. Furthermore, the forms must be created manually in the interface for each module in order to create a configuration module.The new configuration manager aims to simplify the retrieval, storage and management of configuration modules, entries, and values. In addition, we would like a flexible architecture to allow for the addition of new configuration logic and storage mediums with minimal effort. Finally, we want to make the addition of new configuration modules and entries as easy and seamless as possible.
Figure 1 shows what we have done so far.
Configuration Manager
The configuration manager will work as a façade for all underlying services. It will provide an application wide access to configuration management, storage, and retrieval. Current functionalities of the manager are:
- Get all configuration modules,
- Get all configuration modules by category (Process, Generator, Parser, or General), or get only active configuration modules by category,
- Get a configuration module by its fully qualified name,
- Get a configuration entry,
- Get a configuration entry value parsed to the required type,
- Set a configuration entry value,
- Get an algorithm entry,
- Set an algorithm entry.
All of the above are accessed by providing simple arguments. For example, to get a configuration entry, you only have to provide the fully qualified name of the module that holds the entry and the entry key:
configurationManager.getConfigurationEntry (moduleClassName, entryKey);
This is similar for getting active modules by category:
configurationManager.getActiveConfigurationModulesByCategory(ConfigurationCategory.PARSER);
As such, the developer must not know too much about how things work or have to provide an instance of the module or the entry.
Configuration Service and DAO
The configuration service will perform the actual logic of the configuration operations. The reason we chose this architecture is to provide extensibility and reduce complexity. We can now have more services for configurations that do not fit standard configuration behavior (e.g. user access settings, log settings) and provide access to these services via the manager.
Similar to the services, we can have different DAOs for configurations whose table structure differs from regular configurations, or have different storage requirements (e.g. capabilities settings in wpsCapabilitiesSkeleton.xml). This design allow us to have more functionality, support different storage mediums, and at the same time, it reduces coupling and complexity by having clients depend on only one manager and not on multiple services.
Figure 2 shows how we can have two new services and their corresponding DAOs covered by the manager.
Configuration Module
The configuration manager must also enable the configuration modules to identify themselves to the application, publish their configuration parameters, and receive configuration values during runtime. All of this must be done in a type safe manner, i.e. if a configuration module expects an integer, the application must ensure that it receives an integer (e.g. 4) and not a double (4.0) or a string (“4”) or any other invalid type.
Configuration classes identify themselves by implementing the ConfigurationModule interface. Spring will scan and register all classes that implement the ConfigurationModule interface. The ConfigurationService will then communicate with Spring and create a map to these modules. When the ConfigurationManager wants a particular module, it asks the ConfigurationService for it.
Classes that implement the ConfigurationModule will have to provide the following:
- A list of their configuration entries. This can be done through the base class ConfigurationEntry<T>, which is implemented to String, Integer, Double, Boolean, File, and URI. To publish an integer configuration entry, a class would do:
ConfigurationEntry<Integer> entry = new IntegerConfigurationEntry("test.integer.key", "Integer Config Title");
-
This the minimum required and is the key and title for the configuration entry. More information can be provided such as description, required flag, and an initial default value:
ConfigurationEntry<Integer> entry = new IntegerConfigurationEntry("test.integer.key", "Integer Config Title", "Desc", true, 44);
Configuration entries are identified uniquely by the fully qualified name of the module and the entry key, so we only have to make sure that there are no duplicates keys in same module and not worry about other modules’ keys.
- A list of algorithm entries. This is for repositories’ configuration modules only. Null can be returned for others. To publish an algorithm entry, the AlgorithmEntry class can be used:
AlgorithmEntry algorithmEntry1 = new AlgorithmEntry("algorithm1_name", true);
This identifies the algorithm name and the whether it’s active by default.
- The module name to be displayed in the UI.
- Whether the module is active by default or not.
- The category of the module.
The next step is to allow the module to receive configuration values during start up and whenever a new value is entered. To do that, a setter method should be annotated with the entry key. The ConfigurationService will pass the entry value to the setter method (after checking and matching the type). For example:
int someVar; @ConfigurationKey(key = "test.integer.key") public void setSomeVar(int someVar) { this. someVar = someVar; }
Storage
One of the obstacles faced during development was finding the right storage medium for configurations. At first, we attempted to preserve the current mechanism, which uses an XML file to store the configuration modules. However, it soon proved inflexible and not scalable. Therefore, a new approach was needed.
The new approach was to use a light weight database. We surveyed multiple options such as SQLite, H2, and HSQLDB. The choice was narrowed down to H2 and HSQLDB (they’re both pure Java and have official JDBC drivers). Both databases fit very well for our simple needs, so the choice was very much a flip of the coin. We selected HSQLDB. We set it up as an embedded database with memory tables that can be saved to a file and reloaded during startup.
What’s Next?
The next step is to design and implement services and DAOs for configuration modules that do not fit the current configuration service. We plan to implement:
- User configuration module to manage users and access.
- Log configuration module to manage log settings from the UI.
- GetCapabilities configuration module to edit the getCapabilities document information (service provider, service identification).
After that we will implement features, such as process upload and configuration import/export. The last phase will be to implement the web side of the application, which includes the web layer (controller classes) and the presentation layer (the UI).
Leave a Reply