Search code examples
javaspringspring-annotations

Optional @PropertySource location


I'm using Spring 3.2 in a web application and I'd like to have a .properties file within the classpath which contains default values. The user should be able to use JNDI to define a location where another .properties is stored which overrides the default values.

The following works as long as the user has set the configLocation as JNDI property.

@Configuration
@PropertySource({ "classpath:default.properties", "file:${java:comp/env/configLocation}/override.properties" })
public class AppConfig
{
}

However, the external overrides should be optional and so should the JNDI property.

Currently I get an exception (java.io.FileNotFoundException: comp\env\configLocation\app.properties (The system cannot find the path specified) when the JNDI property is missing.

How can I define optional .properties that are used only when the JNDI property (configLocation) is set? Is this even possible with @PropertySource or is there another solution?


Solution

  • Try the following. Create a ApplicationContextInitializer

    In a Web Context: ApplicationContextInitializer<ConfigurableWebApplicationContext> and register it in the web.xml via:

    <context-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>...ContextInitializer</param-value>
    </context-param>
    

    In the ContextInitializer you can add your property files via classpath and file system (haven't tried JNDI though).

      public void initialize(ConfigurableWebApplicationContext applicationContext) {
        String activeProfileName = null;
        String location = null;
    
        try {
          ConfigurableEnvironment environment = applicationContext.getEnvironment();
          String appconfigDir = environment.getProperty(APPCONFIG);
          if (appconfigDir == null ) {
            logger.error("missing property: " + APPCONFIG);
            appconfigDir = "/tmp";
          }
          String[] activeProfiles = environment.getActiveProfiles();
    
          for ( int i = 0; i < activeProfiles.length; i++ ) {
            activeProfileName = activeProfiles[i];
            MutablePropertySources propertySources = environment.getPropertySources();
            location = "file://" + appconfigDir + activeProfileName + ".properties";
            addPropertySource(applicationContext, activeProfileName,
                    location, propertySources);
            location = "classpath:/" + activeProfileName + ".properties";
            addPropertySource(applicationContext, activeProfileName,
                              location, propertySources);
          }
          logger.debug("environment: '{}'", environment.getProperty("env"));
    
        } catch (IOException e) {
          logger.info("could not find properties file for active Spring profile '{}' (tried '{}')", activeProfileName, location);
          e.printStackTrace();
        }
      }
    
      private void addPropertySource(ConfigurableWebApplicationContext applicationContext, String activeProfileName,
                                     String location, MutablePropertySources propertySources) throws IOException {
        Resource resource = applicationContext.getResource(location);
        if ( resource.exists() ) {
          ResourcePropertySource propertySource = new ResourcePropertySource(location);
          propertySources.addLast(propertySource);
        } else {
          logger.info("could not find properties file for active Spring profile '{}' (tried '{}')", activeProfileName, location);
        }
      }
    

    The code above tries to find a property file per active profile (see: How to set active spring 3.1 environment profile via a properites file and not via an env variable or system property)