In this proof of concept https://github.com/DISID/disid-proofs/tree/master/spring-boot-application-own-configuration I show the use of the Spring Boot configuration levels.
Moreover it creates a new PropertySource that lets the application to load their own configuration in an multi-application environment, for example in a Java EE server in which there are multiple applications deployed.
IMHO it would be great if Spring Boot would include the support for application configuration outside the WAR in a multi-application environment.
Feel free to take a look to that proof and evaluate if it is an interesting feature for Spring Boot.
Note the proof of concept only illustrates the feature, it doesn't suggest the way to implement it. If this feature is interesting, feel free to propose the way to implement it in Spring Boot.
Thanks.
I've read through the README in your PoC but I didn't manage to understand how your proposal is different to configuring a Spring Boot application with a custom spring.config.location. spring.config.location is a comma-separated list of locations in which Spring Boot will look for application configuration. Using different locations for each application deployed in the same container would allow different external configuration to be used for each application.
What have I missed?
As far as I know (I read 69.2 Change the location of external properties of an application), spring.config.location is a System property (or environment variable), so all the applications running inside the same JVM and server will get the same value.
The documentation is perhaps a bit misleading. You just have to configure the property before ConfigFileApplicationListener runs. System properties and environment variables are two ways to do that. Configuring the property on your SpringApplication when you create it is another. You could do that via the properties method of SpringApplicationBuilder, for example.
Spring Boot Externalized Configuration feature allow us to package an application in a WAR file only once and deploy the same WAR file in all of the deployment environments: production, etc. By having to configure the spring.config.location in the SpringApplication class we will lose this amazing feature.
The SpringApplication class isn't an external configuration source, to use SpringApplication to set any configuration property will cause the application must to be packaged in a WAR for each environment because we have to set the value of the spring.config.location property for each of the deployment environments.
Why does the external configuration location need to change between environments? As long as each application uses a unique location, the contents of the configuration files in those locations can then vary per environment.
Alternatively, you could use spring.application.name to give each application a custom name for its configuration files. In this case all you're doing is telling Boot to look for custom-name.properties and custom-name-{profile}.properties. It'll still look in all of the usual locations, internal and external, for those files.
These articles explain better than I why the external config needs to change between environments: http://12factor.net/config , https://spring.io/blog/2015/01/30/why-12-factor-application-patterns-microservices-and-cloudfoundry-matter and https://spring.io/blog/2015/01/13/configuring-it-all-out-or-12-factor-app-style-configuration-with-spring
Note that your proposal to use the SpringApplicationBuilder properties method in the SpringApplication class to let each application uses a unique location means that each application must be packaged for each environment ... I think it is better a way to do the same without having to repackage for each environment.
spring.application.name? I suppose you meant spring.config.name, the problem is that spring.config.name is a System property (or environment variable), so all the applications running inside the same JVM and server will get the same value.
I understand why the configuration needs to change per environment. I'm questioning why the _location_ of that configuration needs to change.
Sorry for my fat fingers on my phone
Note that your proposal to use the SpringApplicationBuilder properties method in the SpringApplication class to let each application uses a unique location means that each application must be packaged for each environment
I don't understand why that's the case. Say you have two applications called foo and bar. They're deployed in the same servlet container. foo is configured with spring.config.location=./conf/foo and bar is configured with spring.config.location=./conf/bar. In each environment you put foo's configuration files in ./conf/foo and bar's in ./conf/bar. The _content_ of those configuration files varies according to the environment, but the _location_ remains constant.
spring.application.name? I suppose you meant spring.config.name
Sorry. Yes, I meant spring.config.name.
the problem is that spring.config.name is a System property (or environment variable), so all the applications running inside the same JVM and server will get the same value.
That's not the case. As with spring.config.location, the property just has to be set in the environment before ConfigFileApplicationListener runs. Setting it via SpringApplicationBuilder.propeties will achieve that.
I don't understand why that's the case. Say you have two applications called
fooandbar. They're deployed in the same servlet container.foois configured withspring.config.location=./conf/fooandbaris configured withspring.config.location=./conf/bar. In each environment you putfoo's configuration files in./conf/fooandbar's in./conf/bar. The _content_ of those configuration files varies according to the environment, but the _location_ remains constant.
I think I start to seeing your point of view ...
The problem is that in the organizations that have sysadmins (for example big organizations) the value of the spring.config.location is not known by the development team because it is set by sysadmin team at deployment or at run time. In these cases the applications must be packaged without knowing the value of the spring.config.location.
In this context and as far as I know, the unique way the sysadmins have to set the value of the spring.content.location is by using System properties or environment variables.
I am confused. I haven't followed the discussion in details but if you need to be able to specify that value in some custom way, you can do whatever you want. For instance, you may locate a configuration file or fetch a resource on the network or whatever else in a custom main method and set the value of spring.config.location accordingly.
In this context and as far as I know, the unique way the sysadmins have to set the value of the spring.content.location is by using System properties or environment variables.
As @snicoll has mentioned above, there are other ways to achieve that. For example, in your SpringBootServletInitializer subclass you could use JNDI or the servlet context to retrieve an application-specific configuration setting and then pass that into SpringApplicationBuilder.properties as the value of the spring.config.location property.
You also seem to be overlooking the possibility of using spring.config.name. Consider the two applications foo and bar again. foo has spring.config.name set to foo-application and bar has it set to bar-application. Now in each environment the applications' war files are deployed as normal by the sysadmin and the foo-application.properties and bar-application.properties external configuration files are placed in whatever location the sysadmin has configured. Say the system's using the default locations ("classpath:/,classpath:/config/,file:./,file:./config/). The sysadmin would add both files to the container's config directory:
config/
foo-application.properties
bar-application.properties
Once again, these files would have whatever _content_ is appropriate for the environment.
As @snicoll has mentioned above, there are other ways to achieve that. For example, in your SpringBootServletInitializer subclass you could use JNDI or the servlet context to retrieve an application-specific configuration setting and then pass that into SpringApplicationBuilder.properties as the value of the spring.config.location property.
@wilkinsona Yes, to use JNDI is an option but there are organizations in which sysadmins use properties files for external configuration.
Note these organizations have internal protocols and internal standars, so we cannot force them to use JNDI or whatever else, these organizations say to us (the developer team) the requirements about how the applications must be deployed in their infraestructure.
We are developing Spring Boot based applications for these organizations and we are solving this issue in the way proposed in the proof of concept but I think that extending the Spring Boot particular PropertySource will improve Spring Boot to cover all the cases easily.
You also seem to be overlooking the possibility of using spring.config.name. Consider the two applications foo and bar again. foo has spring.config.name set to foo-application and bar has it set to bar-application.
Yes, you are right. But spring.config.name is similar to spring.config.location, the spring.config.name property is set by the sysadmin team at deployment or at run time, so you cannot include their values in the WAR file.
by the sysadmin and the foo-application.properties and bar-application.properties external configuration files are placed in whatever location the sysadmin has configured.
To summarize the issue:
spring.config.name and spring.config.location at deploy or at run time in order to let the application to load the external configuration.These troublesome is covered by Spring Boot in a fashion way when the applications run alone in their own container.
As I said above, I think that extending the Spring Boot particular PropertySource for those cases in which the applications run in a same servlet container will improve Spring Boot to cover all the cases easily.
Sorry, I think you're still missing the point. Let me have one more try.
Yes, to use JNDI is an option but there are organizations in which sysadmins use properties files for external configuration.
I'm not suggesting that you use JNDI for the external configuration. I said "you could use JNDI or the servlet context to retrieve an application-specific configuration setting and then pass that into SpringApplicationBuilder.properties as the value of the spring.config.location property.". I am suggesting that you use JNDI to externalize the configuration of spring.config.location or spring.config.name so that you do not have to hard code those in your WAR file. All the rest of the configuration is still externalized in one or more properties files. This was to address your concern about the values for spring.config.location or spring.config.name being hardcoded in the war file.
The externalization of the application configuration is based on properties files.
Agreed
The application is packaged once and it is deployed several environments.
Agreed
To achieve that the sysadmin set both the spring.config.name and spring.config.location at deploy or at run time in order to let the application to load the external configuration.
Just to be clear, you don't have to configure spring.config.name or spring.config.location to load external configuration. They only need to be set when there's a possibility of a clash between applications deployed in the same container.
For simplicities sake, I would strongly recommend that you set a custom spring.config.name in the application so that it's consistent in every deployment environment such as dev, QA, and production. If the sys admins really want to control the names of the configuration files then you should introduce some indirection so that you can read that name (from JNDI, servlet context parameter and the like) and pass it into SpringApplicationBuilder.
As I said above, I think that extending the Spring Boot particular PropertySource for those cases in which the applications run in a same servlet container will improve Spring Boot to cover all the cases easily.
There's nothing in your use case that can't already be done today using the techniques I've described above. As such, there's no need for us to add anything to Spring Boot as it just brings extra complexity for no apparent benefit.
I am suggesting that you use JNDI to externalize the configuration of spring.config.location or spring.config.name so that you do not have to hard code those in your WAR file. All the rest of the configuration is still externalized in one or more properties files. This was to address your concern about the values for spring.config.location or spring.config.name being hardcoded in the war file.
...
All the rest of the configuration is still externalized in one or more properties files. This was to address your concern about the values for spring.config.location or spring.config.name being hardcoded in the war file.
I think I understood you. I think you're proposing something like this:
@SpringBootApplication
public class Application extends SpringBootServletInitializer
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// This code is just an example to ilustrate the need of custom code.
// Here it should be added your own way to get the value of the
// `spring.config.location` property from JNDI or from whatever source.
Context ctx;
String springCfgLocation = null;
try {
ctx = new InitialContext();
springCfgLocation = (String) ctx.lookup("java:comp/env/SPRING_CONFIG_LOCATION");
}
catch (NamingException e) {
// TODO
}
// Then add the property and the value of the `spring.config.location`
if(springCfgLocation != null) {
application.properties("spring.config.location=".concat(springCfgLocation));
}
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
If I'm not wrong, in this case the developer has to develop the code to get the value of the spring.config.location property. The problems I see in these proposal are:
spring.config.location.env-entrys in the web.xml, so:env-entry.If the sys admins really want to control the names of the configuration files then you should introduce some indirection so that you can read that name (from JNDI, servlet context parameter and the like) and pass it into SpringApplicationBuilder.
There's nothing in your use case that can't already be done today using the techniques I've described above.
As seen in the previous example and notes, you are forcing the developer to implement an indirection for one case that could be easily supported by Spring Boot.
As such, there's no need for us to add anything to Spring Boot as it just brings extra complexity for no apparent benefit.
Imho, this feature would provide the following benefits:
application.properties sources to be able to load automatically a variant of the application.properties sources and it solves automatically a sub-case of the application.properties sources that it is really useful for some environments.application.properries.The use of JNDI was only raised as one way to address a problem that you raised. It's an edge case at best. Let's not derail the discussion by focussing on it.
You still haven't explained how the functionality that your PoC offers is better than what Spring Boot already offers. Furthermore, as far as I can tell, the edge cases that you have raised apply equally to your PoC.
In the interests of bringing this discussion to a conclusion, let's compare how the two different solutions can be used to solve the problem. To be clear, the problem that we want to solve is providing application-specific externalised configuration when multiple applications are deployed in the same JVM.
With Spring Boot as it is today, one approach is as follows:
SpringApplicationBuilder.properties to configure spring.config.name to an application-specific value. Let's imagine there are two applications involved and they use application-one and application-two.application-one.properties and application-two.properties respectively.SPRING_APPLICATION_CONFIG_LOCATION to /conf./conf/application-one.properties and /conf/application-two.properties files containing configuration that's appropriate for the deployment environment.With your PoC the following would happen:
src/main/resources/application.properties to configure spring.application.name. Let's imagine there are two applications involved and they use application-one and application-two.application-one and application-two respectively.SPRING_APPLICATION_CONFIG_LOCATION to /conf./conf/application-one/application.properties and /conf/application-two/application.properties files containing configuration that's appropriate for the deployment environment.In both cases a configuration setting is hardcoded inside the war file: spring.config.name with what Spring Boot supports today and spring.application.name in your PoC. The _only_ difference between the two approaches is the precise location that is used to hold the configuration files.
The use of JNDI was only raised as one way to address a problem that you raised. It's an edge case at best. Let's not derail the discussion by focussing on it.
Agreed
You still haven't explained how the functionality that your PoC offers is better than what Spring Boot already offers. Furthermore, as far as I can tell, the edge cases that you have raised apply equally to your PoC.
Please note that it is not my intention to propose a better solution than what Spring Boot already offers, I'm just trying to ilustrate what I think it is a case that can be solved by Spring Boot automatically and easily.
In the interests of bringing this discussion to a conclusion, let's compare how the two different solutions can be used to solve the problem. To be clear, the problem that we want to solve is providing application-specific externalised configuration when multiple applications are deployed in the same JVM.
Agreed.
With Spring Boot as it is today, one approach is as follows:
- The developers use SpringApplicationBuilder.properties to configure spring.config.name to an application-specific value. Let's imagine there are two applications involved and they use application-one and application-two.
- The developers provide application-one.war and application-two.war to the sysadmin and tell them that their configuration files need to be named application-one.properties and application-two.properties respectively.
- The sysadmin sets the environment variable SPRING_APPLICATION_CONFIG_LOCATION to /conf.
I suppose you meant SPRING_CONFIG_LOCATION
- The sysadmin creates the /conf/application-one.properties and /conf/application-two.properties files containing configuration that's appropriate for the deployment environment.
Note that the step 1 requires that the developer adds manually the logic to obtain the spring.config.name value from anywhere.
My proposal is that Spring Boot provides a way to load specific application properties automatically.
If the PoC is not an ilustrative example, please take a look to this article: https://spring.io/guides/gs/centralized-configuration/ the concept is the same but without the need of use Spring Cloud Config: provide application-specific externalised configuration automatically when the application is deployed regardless if the application runs in a cloud, in a shared server, or whatever else.
For example, if Spring Boot had a bootstrap process that loads bootstrap properties before the ConfigFileApplicationListener runs then Spring Boot could offer new possibilities for external configuration loading.
Anyway, feel free to take these suggestions and ideas in account, I thought they could be a good Spring Boot improvement :)
Note that the step 1 requires that the developer adds manually the logic to obtain the spring.config.name value from anywhere.
No it doesn't. It's a hardcoded value, just as it is in your example. The _only_ difference is that in one case the value is hardcoded in application.properties and in the other case it's hardcoded in the application's main method.
Thanks for the suggestion, but Spring Boot can already do exactly what you're proposing, in a way that's just as easy as what you're proposing.
Hello eruiz,
I'm starting work in a Spring Boot Application, and I found the same problem as you.
I want to configure an external configuration file but only for my application.
As you say, spring.config.location (or spring.config.file) is shared for all apps running on the same Java EE server.
Have you found one elegant solution?
I was able to solve the problem by defining a specific property application in the server VM arguments:
-Dmyapp.config.location="file:C:/ipex.properties" (or wherever the sysadmin decides)
Then in my spring boot app I do:
@SpringBootApplication
public class MyApp extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
Map<String, Object> myAppProperties = new HashMap<String, Object>();
myAppProperties.put("spring.config.location", System.getProperty("myapp.config.location"));
builder.properties(myAppProperties);
return builder.sources(MyApp.class);
}
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
What do you think?
I hope spring boot will provide a built-in solution to some future release.
Thanks for advance, and sorry if my english isn't very right.
Regards,
Gustavo.
Most helpful comment
Hello eruiz,
I'm starting work in a Spring Boot Application, and I found the same problem as you.
I want to configure an external configuration file but only for my application.
As you say, spring.config.location (or spring.config.file) is shared for all apps running on the same Java EE server.
Have you found one elegant solution?
I was able to solve the problem by defining a specific property application in the server VM arguments:
-Dmyapp.config.location="file:C:/ipex.properties" (or wherever the sysadmin decides)
Then in my spring boot app I do:
What do you think?
I hope spring boot will provide a built-in solution to some future release.
Thanks for advance, and sorry if my english isn't very right.
Regards,
Gustavo.