JavaEE solution configuration best practices
Asked Answered
H

6

14

We build 3-tier enterprise solutions that typically consists of several webapp and ejbjar modules that all talk to a db and have several external integration points.

Each module typically needs its own configurations that can change over the solution's life time. Deploying it becomes a nightmare because now we have 18 property files that must be remembered to copied over and configured also setting up data-sources, queues, memory requirements etc.

I'm hopeful but not optimistic that there can be a better way. Some options we've considered/used, each with it's pros and cons:

  1. Use multiple maven projects and continuous integration (eg. hudson or jenkins) to build a configuration jar that includes all the property files for each environment (dev, qa, prod) and then bundle everything up as an EAR. But then things can't be easily changed in production when needed.
  2. Put most of the settings in the DB and have a simple screen to modify it. Internally we can have a generic configuration service EJB that can read and modify the values. Each module can have a custom extended version that have specific getters and setter.
  3. Version control all the property files then check it out on production and check it into a production branch after making changes.

With all of these you still need to configure data-sources and queues etc. in a container specific way :(

Haag answered 17/11, 2011 at 9:2 Comment(0)
C
0

User a simple database table (Section, Key, Value). Add "Version" if you need it, and wrap the entire thing in a simple ConfigurationService class with methods like getInt(String section, String key)

Not a lot of work, and it makes the application code very neat, and tweaking with the configuration very easy.

Cling answered 17/11, 2011 at 9:52 Comment(5)
And it turns a configuration change into a database change. My current employer's app does this. It's not fun. We replicate databases between environments, and have to special-case the settings. We build different databases for different purposes in dev, and have to shoehorn the settings in. You can't tweak a file and restart if you want to change something, you have to hit the database. I've worked with nice file-based setups before, and preferred them.Giddy
@TomAnderson - explain why changing a database row is more complicated than changing a file. Instead of "replacing databases between envs", you'll have to "replicate config files between envs". Anyway, let's not turn this into a real discussion (perhaps continue on Chat if you want), just saying that they're very similar in nature, and I find DB to be easier to work with for large configs.Cling
This is a better place to discuss, because the discussion is there for future generations to read. Anyway, i didn't mean to say this approach is no good, sorry; just that it comes with its own complications.Giddy
On replication, in our case, we replicate databases anyway, because we need a copy of the production data to test against. But we need to exclude the config table from that, because that needs to be set according to the environment. It's an annoyance. And if i'm making local tweaks to test something, and i refresh my database, i have to go in and put my tweaks in place again. The fundamental problem is that the lifecycles of data and config need to be different, and putting them in the same store forces them to be the same.Giddy
On editing, I find changing the database is harder than changing a file, because i can only do it by running SQL commands. Files i can edit in my IDE, do find-and-replace, sed, grep, etc. I can copy the files to make backups, make changes, then restore the backups. I can have two sets of files, and swap between them to compare their effects. Perhaps if i had a better database tool, i wouldn't find there was such a difference.Giddy
R
2
  1. Сonsider binding a custom configuration object to JNDI. Then lookup this object in your apps to configure them. Benefits - you can use custom configuration object instead of rather generic Map or Properties.
  2. Another way is to use JMX to configure applications you need. Benefits - you can bind objects you have to configure directly to MBean Server and then use such a well-known tools as jconsole or visualvm to configure components of your application.

Both ways support dynamic reconfiguration of your applications at runtime. I would prefer using JMX.

Rounce answered 17/11, 2011 at 11:7 Comment(0)
G
2

I've gone through several cycles of finding ways to do this. I still don't have a definite answer.

The last cycle ended up with a process based on properties files. The idea was that each server instance was configured with a single properties file that configured everything. That file was read by the startup scripts, to set memory parameters, by the app server, and by the application itself.

The key thing, though, was that this file was not managed directly. Rather, it was a product of the build process. We had a range of files for different purposes, kept in version control, and a build step which merged the appropriate ones. This lets you factor out commonalities that are shared along various axes.

For example, we had development, continuous integration, QA, UAT, staging, and production environments, each with its own database. Servers in different environments needed different database settings, but each server in a given environment used the same settings. So, there was something like a development-db.properties, qa-db.properties, and so on. In each environment, we had several kinds of servers - web servers, content management servers, batch process servers, etc. Each had JVM settings, for heap size and so on, that were different to other kinds of servers, but consistent between servers across environments. So, we had something like web-jvm.properties, cms-jvm.properties, batch-jvm.properties, and so on. We also had a way to have overrides for specific systems - production-cms-jvm.properties sort of thing. We also had a common.properties that set common properties, and sensible defaults which could be overridden where needed.

Our build process was actually a bit more complicated than just picking the right options from each set; we had a master file for each server in each environment which specified which other files to include. We allowed files to specify other files to include, so we could build a graph of imports to maximise reuse.

It ended up being quite complicated. Too complicated, i think. But it did work, and it did make it very, very easy to make changes affecting many servers in a controlled way. We even merged a set of input files from development, and another from operations, which contained sensitive information. It was a very flexible approach.

Giddy answered 21/3, 2012 at 18:51 Comment(0)
D
2

I know this has already been answered and my answer is not necessarily generic, but here's my take on things:

Note, here I'm only considering system/resource properties, not application settings. In my view, application settings (such as a payment threshold or other settings should be stored in a database, so that the system can be reconfigured without having to restart a service or cause downtime by re-deploying or re-reading a properties file).

For settings that impact on how different parts of a system connect with each other (such as web service endpoints, etc), I would make use of the JNDI tree.

Database connectivity and JMS connectivity would then be set-up using the Websphere console and can be managed by the Websphere administrators. These can also be created as JACL scripts which can be put into version control if necessary.

In addition to the JNDI resources, for additional properties, such as usernames for web service calls to a backend, etc, I would use Websphere "Name Space Bindings". These bindings can be edited using the Websphere console and accessed via JNDI using the "cell/persistent/mypassword" name.

So I could create the "mypassword" binding (a string), and the management for it falls to the Websphere admin (away from developer eyes or other people who should not have access to production systems), while the same EAR file can be used on dev, test, preproduction and production (which is preferable to have different EAR files for different systems, as the likelihood of other differences creeping in is reduced).

The Java code would then use a simple JNDI lookup (and possibly cache the value in memory).

Advantages over properties files:

  • Not having a "vulnerable" file that would need to be secured because system properties contain passwords.
  • Not having to add Java security policies to allow access to that file location

Advantages over database properties:

  • Not tied to having one database tied to an application server.

Hope that helps

Deloresdeloria answered 27/3, 2012 at 13:15 Comment(0)
V
1

Use multiple maven projects and continuous integration (eg. hudson or jenkins) to build a configuration jar that includes all the property files for each environment (dev, qa, prod) and then bundle everything up as an EAR. But then things can't be easily changed in production when needed.

I think the config should be in the database of the application instance. Your local machine config may be diffrent to dev and to QA, PROD , DR etc.

What you need is a way of getting the config out the database in a simple way.

I create a separate project with a provided dependency of Apache commons-configuration It has many ways of storing data, but I like databases and the configurations lives in the database environment.

    import javax.sql.DataSource;
    import org.apache.commons.configuration.DatabaseConfiguration;

    public class MYConfig extends DatabaseConfiguration {

        public MYConfig(DataSource datasource) {
            super(datasource, "TABLE_CONFIG", "PROP_KEY", "PROP_VALUE");
        }
    }

Put most of the settings in the DB and have a simple screen to modify it. Internally we can have a generic configuration service EJB that can read and modify the values. Each module can have a custom extended version that have specific getters and setter.

Commons configurations as a simple API, you may then write the GUI as you wish. You can do the interface in anyway you wish. Or as a quick win have no interface.

Version control all the property files then check it out on production and check it into a production branch after making changes.

Version control is great. Add another DatabaseConfiguration using composition. The class you extends is the active config and the composed one being the audit. There is another constructor can can have a version. Just overload the right methods to get the desired effect.

    import javax.sql.DataSource;
    import org.apache.commons.configuration.DatabaseConfiguration;

    public class MYConfig extends DatabaseConfiguration {
        final DatabaseConfiguration audit;

        public MYConfig(DataSource datasource) {
            super(datasource, "TABLE_CONFIG", "PROP_KEY", "PROP_VALUE");

            audit = new DatabaseConfiguration("TABLE_CONFIG_AUDIT", "PROP_KEY", "PROP_VALUE");
        }
        @Override
        public void addProperty(String key, Object value) {
            Object wasValue = super.getProperty(key);
            super.addProperty(key, value); 
            audit.put(key,wasValue);//add version code
        }
    }

http://commons.apache.org/proper/commons-configuration/

Villainy answered 10/7, 2013 at 12:24 Comment(0)
C
0

User a simple database table (Section, Key, Value). Add "Version" if you need it, and wrap the entire thing in a simple ConfigurationService class with methods like getInt(String section, String key)

Not a lot of work, and it makes the application code very neat, and tweaking with the configuration very easy.

Cling answered 17/11, 2011 at 9:52 Comment(5)
And it turns a configuration change into a database change. My current employer's app does this. It's not fun. We replicate databases between environments, and have to special-case the settings. We build different databases for different purposes in dev, and have to shoehorn the settings in. You can't tweak a file and restart if you want to change something, you have to hit the database. I've worked with nice file-based setups before, and preferred them.Giddy
@TomAnderson - explain why changing a database row is more complicated than changing a file. Instead of "replacing databases between envs", you'll have to "replicate config files between envs". Anyway, let's not turn this into a real discussion (perhaps continue on Chat if you want), just saying that they're very similar in nature, and I find DB to be easier to work with for large configs.Cling
This is a better place to discuss, because the discussion is there for future generations to read. Anyway, i didn't mean to say this approach is no good, sorry; just that it comes with its own complications.Giddy
On replication, in our case, we replicate databases anyway, because we need a copy of the production data to test against. But we need to exclude the config table from that, because that needs to be set according to the environment. It's an annoyance. And if i'm making local tweaks to test something, and i refresh my database, i have to go in and put my tweaks in place again. The fundamental problem is that the lifecycles of data and config need to be different, and putting them in the same store forces them to be the same.Giddy
On editing, I find changing the database is harder than changing a file, because i can only do it by running SQL commands. Files i can edit in my IDE, do find-and-replace, sed, grep, etc. I can copy the files to make backups, make changes, then restore the backups. I can have two sets of files, and swap between them to compare their effects. Perhaps if i had a better database tool, i wouldn't find there was such a difference.Giddy
H
0

Interesting alternative config file format: write a scala trait. Your config file can then just be a scala file that you compile and evaluate when the server starts. http://robey.lag.net//2012/03/26/why-config.html

Haag answered 27/3, 2012 at 12:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.