Spring Cloud Configuration - Access shared properties file in all microservices - java

Inside my spring cloud config server application.properties. I have passed #EnableConfigServer in my application class
spring.application.name=CONFIG_SERVER
server.port=1080
spring.cloud.config.server.git.uri=PATH_TO_GITHUB_REPO
spring.cloud.config.server.git.username=USNM
spring.cloud.config.server.git.password=PWD
spring.cloud.config.server.git.skip-ssl-validation=true
Inside my git repo application.properties
third-party-cred=MY_VALUE
In my spring cloud config client bootstrap.properties
server.port=1081
spring.application.name=MY_SERVICE
spring.cloud.config.uri=http://localhost:1080
I am trying to access property present in git repo using #Value inside my microservice but it is giving error Could not resolve placeholder 'third-party-cred' in value "${third-party-cred}"

Solution
bootstrap.properties is not enable by default. Please add this dependency inside your microservice.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

Related

Override properties from Azure App Configuration Store

TL;DR
How to have system properties on CLI or environment variables override properties that are provided by an Azure App Configuration Store? Or is this maybe not possible at all?
Longer story
Let us assume a property named app.prop. Let us further assume the following entry in application.yml or in application-<profile>.yml:
app:
prop: Default
Usually, you are able to start the Spring Boot application and provide a system property (e.g. -Dapp.prop=SYS) or an environment variable (e.g. export APP_PROP=ENV) with the effect that the latter overrides the value of the YML config files. If you - for example - provided the environment variable, your application has the value ENV for the property app.prop.
When reading the same property from an Azure App Configuration Store, you can provide a system property or an environment variable as you like. But the value is not overridden anymore; it is the value that is stored in the Azure App Configuration Store.
Some code
I am using Spring Boot version 2.5.7:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.7</version>
</parent>
Further, I am using the following library for accessing the Azure App Configuration Store:
<properties>
<azure-spring-cloud.version>2.7.0</azure-spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-cloud-dependencies</artifactId>
<version>${azure-spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-cloud-appconfiguration-config</artifactId>
</dependency>
</dependencies>
Additionally, for starting the application, I am providing the following property:
spring.cloud.azure.appconfiguration.stores[0].connection-string = ...
This all works very well. In the Azure App Configuration Store, I have the following property:
app.prop = Azure
If now starting the application with the following environment variable APP_PROP = ENV, the value of the property app.prop is still Azure, and not ENV.
Is there any setting missing, so that I can have the same behavior that I had without the above mentioned library?
Actually, I searched a lot, but did not find anything except for some statements regarding overriding values of remote properties in the Spring Cloud documentation, which is not really my case (I am using Azure App Configuration Store).
The whole point of using Azure App Configuration is to store your config in one place and easily manage it without redeploying / restarting the app. Therefore I don't think this is should be even possible.
I would recommend to use labels to load specific version of your prop based on labels data. Few cases:
If you need this property only locally, don't specify it in App Configuration.
If you need multiple versions of it, then just create same property with multiple labels and use your spring.profile (or other conf-property) to distinguish the version.
If you need to load multiple versions of it, load multiple labels:
As described here: https://learn.microsoft.com/en-us/java/api/overview/azure/spring-cloud-starter-appconfiguration-config-readme?view=azure-java-stable#load-from-multiple-labels
You can use this sample to see how it works:
https://github.com/Azure/azure-sdk-for-java/tree/azure-spring-boot_3.6.0/sdk/appconfiguration/spring-cloud-starter-azure-appconfiguration-config

Spring Boot - Initialization Order of Dependencies

I have a Spring Boot Project which uses Jasypt for encrypting properties in the application.yml file.
Jasypt has always initialized and decrypted the passwords, before any dependecy which uses the decrypted password, asked for it.
However I now want to use Azure Key Vault aswell. This dependency now tries to access its properties before they have been decrypted by Jasypt.
How can I change the order in which Spring Boot initializes these dependencies?
For both dependencies I do not have defined any #Bean in the application.
I have created a Demo-Repository.
Its a plain Spring Boot Project. Only thing which is changed is the following:
Added both dependencies in pom.xml:
<!-- Azure Key Vault -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-keyvault-secrets-spring-boot-starter</artifactId>
<version>2.2.5</version>
</dependency>
<!-- Jasypt -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
And in application.yml:
jasypt:
encryptor:
password: test
azure:
keyvault:
enabled: true
uri: ENC(1pGS+OSU9a9Bs+2iAjhyVd8NonXkLp0BsPBOuUzcyJSFnABs+bc5jw==)
client-id: test
client-key: test
tenant-id: test
The Error:
java.lang.IllegalArgumentException: The Azure Key Vault url is malformed.
(...)
Caused by: java.net.MalformedURLException: no protocol: ENC(1pGS+OSU9a9Bs+2iAjhyVd8NonXkLp0BsPBOuUzcyJSFnABs+bc5jw==)
As you can see, first Azure Key Vault Initializes itself, tries to use the azure.keyvault.uri but Jasypt hasn't decrypted it yet.
What I would expect in this case is that it tries to connect but isn't able to connect because the URL doesn't exist. But it atleast should use the decrypted version.
I am greatful for any suggestions.
I actually found the solution with the help of the Jasypt docs on GitHub (Section: Custom Environment)
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//Doesn't work
//SpringApplication.run(DemoApplication.class, args);
//This works:
new SpringApplicationBuilder().environment(new StandardEncryptableEnvironment())
.sources(DemoApplication.class).run(args);
}
}
(...) While not required in most scenarios could be useful when customizing Spring Boot's init behavior or integrating with certain capabilities that are configured very early, such as Logging configuration. (...)
The important part is that you specify the StandardEncryptableEnvironment.
This will make it that the very first thing Spring (Jasypt) does is decrypt the Variables. Everything else stays in the same order.

Spring Boot - How to define application.yml properties as application.properties

I am currently trying to set up an s3 bucket with my Spring Boot webapp for adding/removing images.
The guide I am following uses the following application.yml properties:
amazonProperties:
endpointUrl: https://s3.us-east-2.amazonaws.com
accessKey: XXXXXXXXXXXXXXXXX
secretKey: XXXXXXXXXXXXXXXXXXXXXXXXXX
bucketName: your-bucket-name
How can I define these properties in my application.properties file?
All help is very much appreciated, thank you!
Try to just split them in different lines in your application.properties:
amazonProperties.endpointUrl= https://s3.us-east-2.amazonaws.com
amazonProperties.accessKey= XXXXXXXXXXXXXXXXX
amazonProperties.secretKey= XXXXXXXXXXXXXXXXXXXXXXXXXX
amazonProperties.bucketName= your-bucket-name
if you're using spring Spring Cloud AWS maven dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws</artifactId>
</dependency>
use the following properties in your application.properties file.
cloud.aws.region.static=your region
cloud.aws.credentials.accessKey=your access key
cloud.aws.credentials.secretKey=your secret key
app.awsServices.bucketName=bucketName
refer: https://cloud.spring.io/spring-cloud-static/spring-cloud-aws/1.2.3.RELEASE/multi/multi__basic_setup.html

How to externalize application.properties files in Spring Boot to e.g. external dependent JAR?

I have a simple Maven module (not a Spring Boot application) in which I have placed my application.properties file.
I have 6-7 Spring Boot applications and I don't want to have an application.properties file in each and every application directory. I prefer it, if it is at one single place (external Maven module).
I am adding the maven module as a dependency in each of those Spring Boot application poms.
But, when I run those applications, it is not able to auto-detect the application.properties file because it is coming from a dependent jar not present physically in each of their application directories.
Is there any way to make this possible? I would like to avoid having properties files in 6-7 different locations, because that becomes tough to manage and handle.
Thank you in advance!
Consider using Spring Cloud Config that provides server and client-side support for externalized configuration in a distributed system. It requires some small effort, but it is very useful in long term. Config server manages configuration files (.properties or .yml), you can still use different config per profile (e.g. application-test.properties, application-prod.properties etc.). Your application has a higher priority, so you can always override properties coming from config server if needed. Another cool feature is that config server can utilize Git repository, so you can easily version all your configuration files. It also supports encryption - any fragile data can be encrypted so only your application knows how to decrypt it.
Config server
Config server is nothing else than a simple Spring Boot application that can be implemented as:
#SpringBootApplication
#EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}
with simple application.properties file included:
server.port: 8888
spring.cloud.config.server.git.uri: file://${user.home}/config-repo
with dependency in pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
Config client
On client side you add a dependency to your pom.xml (or its equivalent in build.gradle if you use Gradle):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
and all you have to do is add a URL to config server to your application.properties (or application.yml if you use YAML insted):
spring.cloud.config.uri: http://myconfigserver.com
Config files structure
Now let's say you have set up Git repository for your configuration files. Let's assume that your applications are named like horus, venus, mercury etc. and you have 3 different profiles: dev, test and prod. You also have some configuration that is common for all applications. In this case your configuration files structure would look like this (I will use properties files here but it applies to YAML as well):
application.properties - common config for all apps no matter what profile they use
application-dev.properties - common config for all apps running with dev profile
application-test.properties - common config for all apps running with test profile
application-prod.properties - common config for all apps running with prod profile
horus.properties - horus app config for, common for all profiles
horus-dev.properties - horus app config for dev profile only
horus-test.properties - horus app config for test profile only
horus-prod.properties - horus app config for prod profile only
etc.
There are some additional options that can be set (like encryption, connection strategy (fail fast or ignore) etc.), everything is well described and documented in official documentation https://cloud.spring.io/spring-cloud-config/ Hope it helps you making a good choice for managing your configuration in distributed application environment. Config server is a solution that was invented to solve this problem.
While Szymon Stepniak's answer certainly is a "by-the-book" of Spring Boot answer, I understand your situation, and even tried to do what you try to do by myself. Indeed, you can't define application.properties in other "external modules".
Here is how I've solved it:
Create a configuration in the "common" module
Create a property file in src/main/resources. It shouldn't be named application properties, It's better to provide a unique name for it (at least this is how I've done it, so let's assume that the file is called application-common.properties)
Use #PropertySources annotation to define a property file and load it with configuration.
Here is an example:
package com.myapp.common;
#Configuration
#PropertySources({
#PropertySource("classpath:application-common.properties")
})
public class MyConfiguration {
// you don't really have to define beans
}
Now if you want this configuration to load automatically only because the dependency is defined in your spring boot module of your build system, I've found the best to utilize spring factories:
Create the file src/main/resources/META-INF/spring.factories
Place the following into this file:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.myapp.common.MyConfiguration

Spring boot inject mongodb datasource

I am trying to deploy a spring boot application connected to a mongodb instance to cloud foundry.
cf create-service MongoService default my-mongo
cf push myapp --no-start
cf bind-service myapp my-mongo
cf start myapp
The connection details to the mongodb instance are in the VCAP_SERVICES environment variable. When deploying my application to cloudfoundry spring boot is trying to access mongodb on localhost:27017 and obviously fails.
I would like to parse the VCAP_SERVICES environment variable, construct some mongodb connection details from it and provide this as a spring bean. Which class should I use for these configuration details?
With Spring Boot, you don't need to manually parse VCAP_SERVICES. If you are using MongoTemplate or MongoRepository, it will automatically connect to the bound instance.
Make sure that you have spring-boot-starter-parent identified as the parent artifact in your pom.xml.
You can add the following to your pom.xml to ensure that the cloud connector code is getting picked up:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cloud-connectors</artifactId>
</dependency>
Of course, you also need the MongoDB Spring Data dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
You may want to try setting up the configuration parameters, like a workaround I had to implement with MySQL, check this question.
For me it all came down to properly defining following properties:
spring:
datasource:
url: jdbc:mysql://${vcap.services.mydb.credentials.host}:${vcap.services.mydb.credentials.port}/${vcap.services.mydb.credentials.name}
driver-class-name: com.mysql.jdbc.Driver
username: ${vcap.services.mydb.credentials.user}
password: ${vcap.services.mydb.credentials.password}

Categories