Spring Boot Environment-specific configurations - spring-mvc

I have a spring boot application that uses the actuator, auto-configuration and JPA. I want to be able to use an in-memory DB in my test profile, a MySQL DB configuration during development and a separate production DB configuration when the app is deployed in production. Presumably from the java command line I should be able to specify the environment and the right configuration file or config block in application.properties (or .yml) will be picked up.
I have not found a good post with example describing how to do this switching so I thought I'd ask if anyone has a good example. My main aim is to pre-define the spring.datasource and spring.jpa properties at build time and then at run-time switch the app config per environment "dynamically" using the java command line argument. Secondary goal would be to do the same with the management configurations, etc.
Thank you.

Thanks to #Richard for the mention of spring.profiles.active JVM variable. Since my question was specific to the way Spring Boot does this and since there is much more to the answer, I am inclined to answer this myself and include all the details of how I arrived at the answer in the hopes that it will save others time.
First, you can indeed pick the correct profile on the java command line by adding -Dspring.profiles.active=profile_name when you are running your Spring Boot app. (this is assuming your deployment preference is an uber jar with embedded container - Tomcat in my case)
I wanted to leave MySQL datasource configurations under the default profile and put H2 in-memory datasource configuration under a test profile. However, the way Spring Boot picks the right datasource based on profile is not so obvious. Even though I had MySQL details under the default profile and I had the in-memory H2 datasource details under the test profile, it would still pick H2 as the datasource even when spring.profiles.active was omitted from the command line. This was contrary to my assumption that default profile will be picked, well, by default :-)
I ended up having to put H2 configuration under the default profile and then create a local profile that included the MySQL datasource configuration. Here's what I ended up with in my application.yml
spring:
profiles: default
spring:
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:sampletest;MODE=MySQL
---
spring:
profiles: test
spring.jpa:
hibernate:
ddl-auto: create-drop
---
spring:
profiles: local
spring.datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1/sampledev
username: sample
password: sample
spring.jpa:
hibernate:
dialect: org.hibernate.dialect.MySQLInnoDBDialect
ddl-auto: update
This worked. I was able to switch between default profiles and the local profile by omitting or adding the -Dspring.profiles.active=local on the java command line. Because test profile inherits from default it is also using H2
One more nuance: I added ddl-auto: create-drop to the test profile which uses the in-memory DB to facilitate automatic table creation / teardown for unit tests. But for the local profile which uses MySQL I changed it to update. Implication being that for the local profile I have to first create the database outside of the application.

this article shows how to use spring profiles, available in spring 3.1 and later. It will do exactly what you want.
http://chariotsolutions.com/blog/post/spring-3-1-environment-profiles-2/
set a JVM variable like this: spring.profiles.active=development
then in your configuration xml you can wrap environment specific xml with the profile tags
<beans profiles="development">
<bean id="dataSource" class="..."></bean>
<bean id="messagingProvider" class="..."></bean>
</beans>
You can also set the profile on annotation-driven classes with #Profile("development") at the beginning of the class. That class will only be autowired if the profile matches.
For unit tests you can set the active profile on a test class with #ActiveProfile(profiles = "test", "CI"), it will run using test and CI resources

Related

Generated test can't read property from application.yml

Just trying hands with Spring Cloud Contract. While running the generated test on provider side and when the application context is instantiated, it is unable to read config values from application.yml. When i move the test from generated build folder to src/test/java then issue is not seen anymore.
Which implies since build folder is outside of project src/.. structure, it can't read the config.
How can i fix it?
How do you access the value from application.yml
Let's suppose in application.yml you have the following content:
example:
baseUri: https://jsonplaceholder.typicode.com
You can simply access it in your test using:
#Value("${example.baseUri}")
String exampleBaseUri;
Additionally, if you want a profile just for tests you can create a file application-test.yml where you add properties. To access it values from this file you need to add before your test class:
#ActiveProfiles("test")

Quarkus Flyway Placeholders Configuration issue

I am having trouble getting quarkus.flyway.placeholders working in my Quarkus app.
I have this line defined in my application.properties file
quarkus.flyway.placeholders.myuser=my_user
in my sql file I have this line
GRANT DELETE, INSERT, SELECT, UPDATE ON survey.answers TO ${myuser};
the error I'm getting is
org.flywaydb.core.api.FlywayException: No value provided for placeholder: ${myuser}. Check your configuration!
Here are the things I've tried:
upgraded to Quarkus 1.13.6.Final
Tried setting
quarkus.flyway.placeholder-prefix=#[
quarkus.flyway.placeholder-suffix=]
As shown in the integration test:
https://github.com/quarkusio/quarkus/tree/main/integration-tests/flyway
Thank you
Matthew
I see you have a typo in your configuration or sql script.
With the following settings:
quarkus.flyway.placeholder-prefix=#[
quarkus.flyway.placeholder-suffix=]
In your sql file the placeholder should be defined as a #[myuser], not $[myuser]
You can also change the placeholder-prefix definition in your application.properties file to support prefix you already have in the sql file.
At one time I have two flyway users.
quarkus.flyway.owner this one has create privileges
quarkus.flyway.user this one has fewer privileges.
This item was not associated with the user role.
quarkus.flyway.placeholders.myuser=my_user
After changing it to quarkus.flyway.owner.placeholders.myuser=my_user
it started working.

How to differentiate Databases in ASP.Net Core 2.1 Application based on Enviroment?

I am building this ASP.Net Core MVC Application. Thing is I want to keep three different environments for my Databases, Like Development, Sandbox & Production. Is there any way I can mention that in my app setting file or will I need to manually specify in the deployment like I normally do? Like I am deploying a self-contained application on Elastic Beanstalk & for now, I have edited the appsettings.json file with the required database but that is just like one DB at a time.
"ConnectionStrings": {
"DefaultConnection": "Server = tcp:<Remote DB Server>,1433; Database = <DB>; User Id = <DB Username>; Password = <DB PASS>;"
TL:DR; Can I have multiple Connection strings as per the enviroment in my appsettings.json file?
As per the configuration by environment chapter of the official documentation, you have a few options for this:
To load configuration by environment, we recommend:
appsettings files (appsettings.<>.json). See Configuration: File configuration provider.
environment variables (set on each system where the app is hosted). See Configuration: File configuration provider and Safe storage of app secrets in development: Environment variables.
Secret Manager (in the Development environment only). See Safe storage of app secrets in development in ASP.NET Core.
The first option is a very common solution for environment-specific configuration and simply involves in additional appsettings.json files that include the environment name in the file name. The default templates already come with a appsettings.Development.json file that is only loaded for the Development environment. Similarly, you could create a appsettings.Sandbox.json and a appsettings.Production.json file that are loaded with the Sandbox and Production environment respectively.
The configuration files are loaded in addition to the normal appsettings.json file, so you can use that to specify general defaults and only overwrite environment-specific things in the environment-specific appsettings.<Environment>.json files.
Note that you should always try to avoid putting production secrets in files, especially those that are committed to source control. For those, you can also use environment variables to overwrite specific values. For example, an environment variable ConnectionStrings__DefaultConnection could contain the connection string for your application and would overwrite what is configured in one of the appsettings files.

How to deploy configfile before bundles

Given a feature with a <configfile> and a <bundle>, how can I ensure that the file is deployed before the bundle? What I'm seeing is that my bundle gets started first and the file deployed second (even if <configfile> is the first tag).
I guess this is could be the <bundle> being considered a pre-requisite so it makes sense to start that before processing the rest of the <feature>?
Actually if you are doing it right, your bundles shouldn't care if the configuration is available or not. The service should either not be started or with a default setting in case no "external" configuration has been set. In case of a new configuration the service will be restarted with the new configuration.

How to configure WCF in a separate dll project

I'm developing a web application (ASP.NET 3.5) that will consume a number of web services. I have created a separate dll-project for each web service: these projects contains the service reference and client code.
However, the calling website MUST have the <system.serviceModel> information (the <bindings> and <client> nodes) in it's web.config, even though this information is also in the dll's app.config file! I have tried copying the serviceclass.dll.config over to the bin directory of the website, but this didn't help.
Is there any way to centralize the configuration of a WCF client?
I've only limited WCF experience, all with BasicHTTP bindings. But I'm allergic to WCF's xml files and have managed to avoid them thus far. I don't recomend this generally but I put the configuration details in my apps existing configuration store and then apply them programatically. E.g. With a Web service proxy I use the constructor for the Client that takes 'bindings'and 'endpoint' and programatically apply the settings to the bindings & endpoint.
A more elegent solution appears to be descibed here: Reading WCF Configuration from a Custom Location, but I haven't tried it yet.
From my experience, library projects never read app.config.
So you can really delete the file because it is not used. The library's host configuration is read instead, so that is the only place the endpoint and binding configuration should be.
It's possible to forgo xml config and build up the Binding and Endpoint classes associated with the service in the constructor or a custom "Service Factory". iDesign has some good information on this:
http://www.idesign.net/idesign/DesktopDefault.aspx?tabindex=5&tabid=11
(See In Proc Factory)
In their approach, you set attributes on your services to specify at a high level how they should work (ie [Internet], [Intranet], [BusinessToBusiness]), and the service factory configures the service according to best practices for each scenario. Their book describes building this sort of service:
http://www.amazon.com/Programming-WCF-Services-Juval-Lowy/dp/0596526997
If you just want to share configuration XML config, maybe use the configSource attribute to specify a path for configuration: http://weblogs.asp.net/cibrax/archive/2007/07/24/configsource-attribute-on-system-servicemodel-section.aspx
Remember that a configuration file is is read by an executable that has an entry point. A library dll does not have an entry point so it is not the assembly that will read it. The executing assembly must have a configuration file to read.
If you would like to centralize your web configs then I would suggest you look into nesting them in IIS with virtual directories. This will allow you to use the configuration inheritance to centralize whatever you need.
There are 2 options.
Option 1. Working with channels.
If you are working with channels directly, .NET 4.0 and .NET 4.5 has the ConfigurationChannelFactory. The example on MSDN looks like this:
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = "Test.config";
Configuration newConfiguration = ConfigurationManager.OpenMappedExeConfiguration(
fileMap,
ConfigurationUserLevel.None);
ConfigurationChannelFactory<ICalculatorChannel> factory1 =
new ConfigurationChannelFactory<ICalculatorChannel>(
"endpoint1",
newConfiguration,
new EndpointAddress("http://localhost:8000/servicemodelsamples/service"));
ICalculatorChannel client1 = factory1.CreateChannel();
As pointed out by Langdon, you can use the endpoint address from the configuration file by simply passing in null, like this:
var factory1 = new ConfigurationChannelFactory<ICalculatorChannel>(
"endpoint1",
newConfiguration,
null);
ICalculatorChannel client1 = factory1.CreateChannel();
This is discussed in the MSDN documentation.
Option 2. Working with proxies.
If you're working with code-generated proxies, you can read the config file and load a ServiceModelSectionGroup. There is a bit more work involved than simply using the ConfigurationChannelFactory but at least you can continue using the generated proxy (that under the hood uses a ChannelFactory and manages the IChannelFactory for you.
Pablo Cibraro shows a nice example of this here: Getting WCF Bindings and Behaviors from any config source
First of all class libraries (DLLs) do not have their own configuration, however they can read the configuration of their host (Web/Executable etc.). That being said, I still maintain an app.config file on the library projects as a template and easy reference.
As far as the service configuration itself is concerned, WCF configuration can make somebody easily pull their hair out. It is an over-engineered over-complicated piece. The goal of your applications should be to depend least on the configuration, while maintaining flexibility of deployment scenarios your product is going to come across.

Resources