Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend PropertyPlaceholderConfigurer to choose property file based on runtime environment [SPR-1876] #6570

Closed
spring-projects-issues opened this issue Apr 10, 2006 · 31 comments

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Apr 10, 2006

Chris Lee opened SPR-1876 and commented

PropertyPlaceholderConfigurer can be readily extended to choose the property file to read at runtime, based on the current runtime environment. This simplifies moving the same deployment package (EAR/WAR) between environments w/o having to rebuild (saving time, reducing risk).

See the attached file for a reference implementation (feel free to adjust as necessary).


Affects: 2.1 M4

Attachments:

34 votes, 32 watchers

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 10, 2006

Chris Lee commented

Reference implements - RuntimeEnvironmentPropertiesConfigurer

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 10, 2006

Fabio Grassi commented

Chris has a good point here. I resolved the very same use case he describes having MethodInvokingFactoryBean call System.getProperty and using the returned value to set the location property of PropertyPlaceholderConfigurer.

His solution is obviously much cleaner and at the same time easy to implement and doesn't add too much complexity.

I hope for the adoption of his proposal.

Ciao, Fabio.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 12, 2006

David Medinets commented

I suggest extending the concept even farther:

  • read the configuration name (ie, dev, staging, production) from a text file.
  • read the configuration value from a URL.
  • read the configuration value from a database (ie, provide a datasource and SQL statement)

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 12, 2006

Chris Lee commented

I'll be posting a v2 with a pluggable RuntimeEnvironmentKeyResolver; the default implementation will be equivalent to what is there now. This will allow users to provide their own implementation(s), resolving the current environment in an appropriate manner.

Chris.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 12, 2006

Chris Lee commented

Version 2 is attached; this adds an (optional) RuntimeEnvironmentKeyResolver interface, allowing for custom implementations that determine the runtime environment in an appropriate manner. The default implementation still uses a system property.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 13, 2006

Peter Monks commented

The following thread goes into a bit more detail about why this change is important: http://forum.springframework.org/showthread.php?t=24066

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 13, 2006

Peter Monks commented

Added an environment aware version of ClassPathXmlApplicationContext and another RuntimeEnvironmentKeyResolver implementation.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 20, 2006

Sivan Mozes commented

RuntimeEnvironmentPropertiesConfigurator line 117, empty while loop due to semi-colon

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 20, 2006

Sivan Mozes commented

The above is the 1.4 backport only. Please disregard.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jul 31, 2006

Alex Wei commented

Another approach is to use an EagerPropertyPlaceholderConfigurer to load the properties file specified by a system property.

Please see the link of a related issue:
[link]http://opensource.atlassian.com/projects/spring/browse/SPR-1076[/link]

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 1, 2006

Chris Lee commented

EagerPropertyPlaceholderConfigurer is a orthogonal approach to resolving properties; would be nice to merge the two concepts, so that we have the ability to eagerly load properties and specify (with validation) which environment to load properties for.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Sep 14, 2006

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Sep 15, 2006

Peter Monks commented

Keep in mind that the problem stated here (and the proposed solution) covers more than just property differences between environments - specifically it also covers differences in bean wirings between environments (something that property placeholders do not support by themselves).

This is a reasonably common use case; eg.

  • developer sandbox environments have mock versions of DAOs / web service clients / what-have-you configured but other environments are configured with the real thing
  • integration test environment has a series of "validation shims" (akin to JDK 1.4+ asserts, only more sophisticated) configured but other environments don't
  • performance test environment has a series of "timing shims" configured but other environments don't
    etc. etc.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Sep 18, 2006

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 27, 2008

Clint Shank commented

As an alternative, what about specifying all the properties in a single file. I like this from a maintainer's perspective. All properties are in one file. You could have default properties and override them by environment, only if necessary. The properties file would look like:

  1. this is the default value for all environments
    my.prop=defaultValue

  2. but in the production environment, it is different
    [production].my.prop=productionValue

  3. here's another property with no default
    [development].my.prop2=someDevelopmentValue
    [test].my.prop2=someTestValue
    [production].my.prop2=someProductionValue

The beandef would specify only the property name minus the environment and the Configurator would resolve appropriately:

"${my.prop}"

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Mar 3, 2008

Peter Monks commented

Makes perfect sense in the case where the set of properties isn't too large, although you can still get the same defaulting behaviour using Chris' original implementation (although using multiple files, rather than a single file as you suggest).

If you implement this, would you mind using the same environment key resolver infrastructure? That just makes it easier for users of this functionality to mix and match the various approaches without having to change the way their environments are identified.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 26, 2008

Bob Tiernay commented

I think this should be implemented in the core. Juergen, where you at on this one buddy :)

I have found at least 12-15 articles about this very issue, and Spring needs an official implementation to address it IMO. This is a great start guys!

Anyways, support for multiple properties files with a configurable convention would be nice too. This way you could have something like:

jdbc.development.properties
jdbc.staging.properties
jdbc.production.properties

Sometimes this is needed due to framework constraints while trying to satisfy the DRY principle.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 26, 2008

Antony Stubbs commented

This is definitely required.

Here's my take on the subject from yonks ago:
http://forum.springframework.org/showthread.php?t=46504

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 26, 2008

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jun 15, 2010

Jeff Johnston commented

Here is a link to another similar ticket. I think Configleon solves this problem perfectly!

http://jira.springframework.org/browse/SPR-4902

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 21, 2010

Dave Syer commented

Configleon is opinionated (it has conventional locations and search path for its properties files), and Spring generally speaking is not, so I would prefer a more Spring-like idiom.

You can already do everything Configleon does, and that the RuntimeEnvironmentPropertiesConfigurer attached here does, (I think) with System property resolution in the locations property of a PropertyPlaceholderConfigurer. Spring 3 is needed to get default values of undefined properties. I use something like this all the time:

<context:property-placeholder ignore-resource-not-found="true" system-properties-mode="OVERRIDE"
	location="classpath*:/META-INF/spring/config-global.properties,classpath*:/META-INF/spring/config-${ENVIRONMENT:dev}.properties,classpath*:/META-INF/spring/config-local.properties" />

#12139 and #9578 are also interesting, and would go beyond simple PPC behaviour, and closer to what David Medinets was asking for here. I would prefer to close this issue as Won't Fix and concentrate on the others. Any other opinions?

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 21, 2010

Chris Lee commented

Dave,

The use case of altering the Spring configuration depending on the runtime environment appears (in my experience) to be a common one and worthy of direct support by Spring. #12139 and #9578, while they provide additional PPC capabilites, don't explicitly introduce the concept of a runtime environment.

What David Medinets had requested above (ability to plug in a strategy to determine the current runtime environment) is already address by this proposal via the RuntimeEnvironmentKeyResolver interface - the default being to use a system property, with reasonable alternative implementations being JNDI, text file, etc.

In addition, this proposed solution also validates the configuration, ensuring that a) the set of supported environments is well-defined and b) the set of properties is consistent across all environments.

Seems like there should be layers / ordering here - the base level(this proposal) being determining what the runtime environment is and setting some properties based on that, with higher layers using those properties, for example as #12139 and #9578 suggest.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 21, 2010

Dave Syer commented

Thanks for getting back to me on this. I'm pretty sure we won't disagree too much on the principles here, but the implementation isn't where I want it yet. I've prototyped a solution involving environment-specific configuration (in a branch in SVN if anyone is interested, but it's quite old now so I don't know if it still builds cleanly), and I think the solution has to go much deeper into Spring core code, so I'm not surprised that a simple proposal with two or three new classes doesn't quite go as far as I would like.

There are three main features in the attached code: a key resolver interface, an application context implementation, and a property placeholder configurer extension. All good clean code and expressing their intentions pretty clearly I think. In more detail:

  • I'm not sure RuntimeEnvironmentKeyResolver meets my requirement for an environment since it only returns a single String, whereas in general an environment might be composed of a set of key-value pairs, even if many people only use one key.
  • The other problem I have with the attached code is that it requires users to adopt a new implementation of ApplicationContext. Maybe this is just by way of an example of usage of the environment key, but it is not very convenient for many reasons (not least the fact that many people don't use ClassPathXmlApplicationContext).
  • The property placeholder configurer doesn't seem to add anything over the example I gave, except that the environment name doesn't have to be a System property. The other issues (#12139 in particular) address the case of a more flexible environment definition, so my conclusion is still that this issue is redundant if the others are resolved.
  • I do like the validation option in the proposed placeholder configurer. I think we can use that concept (optionally) somewhere in the environment-specific configuration features in Spring 3.1, but that isn't the main topic of this issue. Feel free to open another issue if that is a priority for you in 3.1.

I think what you call the "base level" is really the RuntimeEnvironmentKeyResolver. If so I would agree and I would call it a bootstrap strategy for declaring and discovering an environment. The discussion of that belongs in #12139 really, but suffice to say here that I would make it resolve (optionally) multiple key-value pairs, and make it discoverable from the classpath (or something TBD) before the ApplicationContext and its BeanFactory are created (although it will be useful to beans defined in the BeanFactory once that exists).

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 21, 2010

Chris Lee commented

Thanks for the prompt response. We are indeed in agreement on the principles. BTW, the attached application context was supplied by Peter Monks as an example of his solution to this issue, which drove the creation of the RuntimeEnvironmentKeyResolver to remove the undesirable dependency on the application context. The final solution is in the last ZIP file.

#12139 is mentioned, yet that issue doesn't seem to mention runtime environment at all; perhaps there are candidate solution(s) for that issue that do overlap with runtime environment resolution?

Looks like the bigger picture scope is:

  • Resolution of the runtime environment in a pluggable manner; examples include pulling the environment from a system property, JNDI, text file, etc.
  • Use of the runtime environment in PPC, @Import, <import> and component scan; you'd mentioned that the environment could be defined as name/value pairs - were you envisioning that the environment definition would be the environment specific properties? e.g. would the following make sense:
<bean ... p:someProperty="#{runtimeEnvironment.databaseUrl}"/>

...alternately, a PPC could readily use the discovered runtime environment to drive its property file resolution (though this doesn't address other areas were environment-specific locations are desired, e.g. component scane, etc). The former has the nice attribute of being explicit about where the value is coming from.

  • validation of environment specific variables. The intent here is to prevent readily-detectable errors, such as forgetting to add a property to an environment to making a typo in doing so. Without validation, the same binary that works in multiple pre-production environments may fail on startup in a production environment due to failing to resolve a property (or arguably worse, assume an inappropriate default value).

If you feel these requirements are addressed by your implementation plans for #12139 and/or other JIRAs, I'd agree that this issue is redundant and can be closed as such.

Chris.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 22, 2010

Chris Lee commented

Dave - have a better understanding of your intentions after reading recent comments to #12139.

There is an important conceptual difference between these two issues - #12139 is about exposing the technical runtime environment (system properties, JNDI attributes, etc), whereas this issue is about selecting a logical runtime environment (dev, staging, production, etc) and basing configuration on that.

A possible solution would be to consider these as two separate (yet collaborative) resolvers:

  1. RuntimeEnvironmentResolver - responsible for determining which logical environment is being used
  2. RuntimeEnvironmentPropertiesResolver - responsible for loading the technical properties for the chosen runtime environment

...the resulting RuntimeEnvironment is exposed for use in bootstrapping & bean definitions. Potential use cases:

<bean p:databaseUrl="#{runtimeEnvironment.properties['databaseUrl']}"/>

<context:property-placeholder ignore-resource-not-found="true" system-properties-mode="OVERRIDE"
	location="classpath*:/META-INF/spring/config-global.properties,classpath*:/META-INF/spring/config-#{runtimeEnvironment.name}.properties,classpath*:/META-INF/spring/config-local.properties" />

<import resource="/resources/#{runtimeEnvironment.name}/themeSource.xml"/>
<import resource="/resources/#{runtimeEnvironment.properties['someProperty']}/themeSource.xml"/>

Chris.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Aug 22, 2010

Dave Syer commented

I like the distinction between logical and physical environment (I don't like the term "technical environment" much, so I'll use "physical"), and I even like the idea of giving the logical environment a name, but you are mistaken about the intent of #12139: it is precisely the logical environment which that issue seeks to expose. The logical environment is used to load the resources that define the physical environment, going beyond XML and properties files to keep up with the more modern styles of dependency injection that Spring now supports.

The prototype already contains the two layers you are discussing (a bootstrap resolver for the logical environment and a property placeholder configurer that resolves and binds from the physical environment). The line is slightly blurred because the source of the logical environment definition can also be the source of the physical environment (e.g. System properties play a role in both in many cases). The usual best practice is to not blur the lines like that, and to package all possible physical environments as part of the application definition (otherwise your validation algorithms wouldn't be so useful), but that doesn't have to be the only choice.

To digress slightly, I would go further and argue that best practice would be also to package the logical environment into some sort of deployment descriptor that goes with the application artifacts into a runtime platform, but that's another story because there is no standard way of doing that. System properties are good enough for farmwork, as well as being the incumbent, so we might as well start there and use them as a default source, but they are not on their own part of a deployable unit (usually).

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 20, 2010

Balaji Varanasi commented

A related problem to this is logging level control. Typically on DEV and Test servers it is desirable to have application logging level set to DEBUG or INFO. And on production servers this usually should be set to ERROR level. It would be nice if there is a way Spring can dynamically change the log levels of the underlying logging implementation based on the environment the application is running.

A year ago, I wrote a custom Log4J listener similar to org.springframework.web.util.Log4jConfigListener to do this environment independent log level control. But I think it would be nice if this can be somehow addressed via the new environment dependent profiles.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 2, 2010

Eric Pederson commented

In #11823 I posted code that we've been using to solve this problem (choose property file based on environment). One thing that it also supports is a third level of overriding properties.

The current Spring PropertyPlaceholderConfigurer supports overriding using system properties. What we have found useful is to have 3 levels:

  • Environment specific
  • Server specific
  • System properties

This way you can have certain servers act specially: for example, they are running Quartz jobs, etc. You could just use system properties but those are not version controlled, etc.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 4, 2010

Chris Beams commented

Eric,

Take a look at EnvironmentAwarePropertyPlaceholderConfigurer, currently in SVN trunk and available via the nightly 3.1.0.BUILD-SNAPSHOT builds. It is a PropertyPlaceholderConfigurer that delegates to Spring 3.1's new Environment abstraction for property resolution. Amongst other functionalities, Environment objects maintain an ordered set of PropertySource objects. By default, this set contains system properties and environment variables (and in the case of a PPC, any properties files/objects that have been injected). This set of PropertySources is configurable and re-orderable. This means you're free to add any source of properties you like, in a first-class way.

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 4, 2010

Eric Pederson commented

Great - thanks Chris!

Loading

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 26, 2011

Chris Beams commented

Resolving as Won't Fix - superseded by functionality in PropertySourcesPlaceholderConfigurer.

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant