views:

460

answers:

3

I have a web application that's based on Spring and the application contexts, applicationContext.xml and myServlet-servlet.xml contain settings that should be configurable by the sysadmin in deployment.

What's the best way to enable changing of settings like [database server details, remote webservice endpoints, etc] without requiring editing of the WAR contents?

Spring provides the PropertyPlaceholderConfigurer which can be used in the beans configurations, but I think that's going to require an absolute path to the properties file, which I'd like to avoid, if for no other reason than to allow multiple instances of the same servlet to run on the same machine.

There's also maybe the option to use JNDI configured resources, though there doesn't appear to be a BeanFactoryPostProcessor implementation out-the-box doing this so it might not be a good way to approach it.

What's the standard best-practice, if there is one, to deal with this sort of requirement?

Related SO entries:

http://stackoverflow.com/questions/372686/how-can-i-specify-system-properties-in-tomcat-configuration-on-startup

+1  A: 

"What's the best way to enable changing of settings like [database server details, remote webservice endpoints, etc] without requiring editing of the WAR contents?"

The only way to do that is to externalize the configuration. You can explode the WAR file, move the .properties file outside the WAR (as long as it's in the CLASSPATH, Spring will find it), or put the modifiable values in a database.

duffymo
Database settings is the best way to go.
Martlark
I'm not sure I agree that putting configuration details, such as database settings, into a database is the best way to go. Bit of a infinite recursion...
ptomli
You have to bootstrap it somewhere. The config database may or may not be the one that the app uses.
duffymo
+1  A: 

You can also achieve this with a file based solution. On each environment define a environment name system property. Then use this name to load an external properties file. The example below loads a default set that is then overrided with a environment specific set.

<bean id="propertyPlaceholderConfigurer"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
    <property name="locations">
            <list>
                    <value>classpath:site/properties/default/placeholder.properties
                    </value>
                    <value>file:///site/properties/${env.name}/placeholder.properties
                    </value>
            </list>
    </property>
</bean>

Adapted from, my answer here

Pablojim
Nice, and doesn't actually require the environment config to be in a fixed root location, /site/properties to just as easy be ${env.applicationRoot}/config. I like
ptomli
A: 

If you don't want to externalize your properties file:

I'm using a prefix that represents my deployment environment in my properties. Example:

#Test url
test.url=http://test.url.com

#Production URL
prod.url=http://prod.url.com

I defined a system property named "entorn" in each environment (-D argument to jvm call in your application server start script). The value of this property is "test" in my test environment and "prod" in my production environment.

Then I defined my "propertyConfigurer" bean:

<bean id="propertyConfigurer" class="es.indra.ccma.config.EnvironmentPropertyPlaceholderConfigurer">
    <property name="locations">
     <list>
      <value>classpath:ccma.properties</value>
     </list>
    </property>
</bean>

The EnvironmentPropertyPlaceholderConfigurer code:

package es.indra.ccma.config;

import java.util.Properties;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

public class EnvironmentPropertyPlaceholderConfigurer extends
     PropertyPlaceholderConfigurer {

    private String environment;
    final private static String ENV_SYSTEM_PROPERTY = "entorn";

    public EnvironmentPropertyPlaceholderConfigurer() {

     environment = System.getProperty(ENV_SYSTEM_PROPERTY);
     if (environment == null) {
      //default environment
      environment = "test";
     }
    }
    protected String resolvePlaceholder(String placeholder, Properties props) {

     final String envPlaceholder = environment + "." + placeholder;
     if (props.containsKey(envPlaceholder)) {
      return props.getProperty(envPlaceholder);
     } else {
      return props.getProperty(placeholder);
     }
    } 
}

If you are running your code in "test" environment and you want to retrieve the value of "url" property, the propertyConfigurer looks for "test.url" in your properties files and if no "test.url" property found it will look for "url" property.

This is not my idea I followed this tutorial to acomplish this.

SourceRebels