Spring & Maven - how to provide custom properties to Spring application - java

I have a Spring application which runs in a web container (Tomcat). This Spring application uses a properties file to find the database JDBC location:
#Configuration
#PropertySource("classpath:app.properties")
public class MyApplication {
}
In app.properties, I have:
database.dataSource.url=jdbc:postgresql://localhost:5432/app
It's now easy to get the value at runtime:
#Component
class DatabaseConfiguration {
#Value("${database.dataSource.url}")
private String URL;
}
So far, so good. Now I am using the cargo-maven2-plugin plugin to deploy the WAR during an integration test. Before the WAR is deployed, an ad-hoc PostgreSQL database is deployed into a Docker container via the docker-maven-plugin plugin. This instance runs on a custom, dynamic port instead of the usual 5432. This port is filled in into the ${database.port} property by the docker-maven-plugin plugin.
This means that I need to somehow alter app.properties on the fly to fill in this port. This seems hacky, so maybe there is a way to provide/override the port via the cargo-maven2-plugin to my Spring application, so I could use that one instead of the one in app.properties?
What is a 'clean' way to achieve this?

Use #TestPropertySource to use a different properties file for testing. https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/TestPropertySource.html

Related

Use Spring Boot Cloud Config to change application.properties in classpath

I have to change my custom defined spring properties (defined via #ConfigurationProperties beans) during runtime of my Spring Boot application.
Is there any elegant way of doing this using Spring Cloud Config?
I don't want to use an external application.properties in a git repository (as the spring boot application gets shipped to customers and I dont' want to create a git repository for everyone of them).
I just want to access and change the local application.properties (the one in the classpath, located in src/main/resources) file in my Spring container or (if thats not possible) in the Spring Cloud Config Server, which I could embed into my Spring Boot app. Is that possible somehow?
BTW: The goal is to create a visual editor for the customers, so that they can change the application.properties during runtime in their spring boot app.
Spring Boot supports profile based application configuration. Just add application-<profile>.properties file. Then just when running the application select a profile depending on the environment making use of spring.profiles.active.
-Dspring.profiles.active=dev
This will run the application with application-dev.properties file (overriding the default application.properties, i.e you can just leave the common stuff in the default file and change the rest depending on the env)
On a side note, having a repo for configuration is not a must. You could just place them in the class path and give a search-location.
spring:
application:
name: config-server
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:configs/
It actually is possible and in the end quite easy to achieve. It just took me a whole day to get all the information together. Maybe this helps someone:
You basically just need Spring Actuator, but for a certain endpoint, you also need the spring cloud dependency. (to make Post requests to the /env endpoint of Spring Actuator)
To alter your config at runtime, just add the following to your application.properties:
management.endpoints.web.exposure.include: env,refresh
management.endpoint.env.post.enabled: true //this property is only available when spring cloud is added as dependency to your project
If you (like me) don't need the feature of an externalized config, then you also have to add the following (otherwise, your Spring app will not start and throw an error that some config is missing)
spring.cloud.config.enabled: false
Now, if you send a POST request to /actuator/env endpoint with an object in the HTTP body in the form of {"name":"...", "value":"..."} (name is the name of a config property), then your config gets changed. To check that, you can do a GET request to /actuator/env/[name_of_config_property] and see that your config property has changed. No need to restart your app.
Don't forget to secure the /actuator endpoint in your SecurityConfig if you use a custom one.
It seems to me that you neither need the #RefreshScope annotation at your config classes nor the /actuator/refresh endpoint to "apply" the config changes.
Maybe what your looking for could be achieved with Spring cloud config and spring cloud bus. It's explained here: https://cloud.spring.io/spring-cloud-config/reference/html/#_push_notifications_and_spring_cloud_bus
In summary, any change on configuration sent an event to the spring cloud bus and you can then reload app context or configuration with new properties.

How to load the properties based on the environment defined in application.properties in Spring application. NOT with Springboot

I'm new to spring and I'm studying it. And stumbled upon the #Profile annotation.
I want to write a simple project with Spring (not Springboot) to learn how to load properties based on the environment using #profile annotation. Almost everywhere, the examples (Ex1, Ex2) I see only with the Springboot. I'm wondering whether we cannot write a Spring application that can dynamically load the properties based on the environment (dev, prod).
Some examples ( Ex3, Ex4, Ex5) show with the #Profile but those have hardcoded the bean details for each environment like below. Is this how we have to write the property loading?
#Profile("dev")
#Bean
public String devDBCcnnection() {
System.out.println(dbConfiguration.getUrl());
return "DB Connection for Dev";
}
#Profile("test")
#Bean
public String devTestCcnnection() {
System.out.println(dbConfiguration.getDriverClassName());
return "DB Connection for Test";
}
#Profile("prod")
#Bean
public String devProdCcnnection() {
System.out.println("DB Connection for Prod");
return "DB Connection for Prod";
}
It has to write a bean for each profile like in the above example?
Can someone tell me using #Profiles, can't dynamically load the property values like in Spring applications?
Appreciate it if you can give the samples with Spring 5
Almost everywhere, the examples (Ex1, Ex2) I see only with the
Springboot. I'm wondering whether we cannot write a Spring application
that can dynamically load the properties based on the environment
(dev, prod).
Spring boot uses the spring context. The spring context allows you to use profiles. Therefore no problem using profiles with simple Spring project (non spring-boot).
There are many ways that you can use Profiles.
One of them is the example that you gave with specific beans that have #Profile and get registered in spring for a specific profile.
Another one, more commonly used in enteprise applications is to ship a jar application with multiple application.yaml files. So for example you ship your application, containing dev-application.yaml and qa-application.yaml. You can then start your application selecting a specific profile to be active. Then that specific application.yaml will be used when the application starts up to build the spring context. So the aplication will be started with qa-application.yaml and will have a connection to the QA database.
But be careful the default application.yaml will also be loaded. The specific application.yaml for example qa-application.yaml will be loaded on top of default application.yaml.
The following article contains very good information about spring profiles
spring profiles article
Considering my example here, I quotte something relevant from that article.
The Default Profile The default profile is always active. Spring Boot
loads all properties in application.yml into the default profile. We
could rename the configuration file to application-default.yml and it
would work the same.
Other profiles will always be evaluated on top of the default profile.
This means that if a property is defined in the default profile, but
not in the qa profile, the property value will be populated from the
default profile. This is very handy for defining default values that
are valid across all profiles.
In order to activate a specific profile
For non spring-boot projects here is a very good answer spring active profile
For spring-boot projects you can
Use a system variable to start your jar file
java -Dspring.profiles.active=qa -jar myApp.jar
Use an environment property to start your jar file
export SPRING_PROFILES_ACTIVE=qa
java -jar myApp.jar

Multiple property files for same Spring Boot Application based on request

I have a Spring Boot application for UI test automation using Cucumber and Selenium.
The application is expected to test multiple environments.
To begin with I have created json files with required properties like URLs, credentials etc. and load it(pass the file path as a property and use it) while triggering the test (mvn test -DconfigFile=config/env1_config.json).
I see that using profiles while running test is an option-Dspring.profiles.active=client1 but as i will configure multiple pipelines in Jenkins for testing multiple environments to use same project with different configuration files, it will clone the entire project and run tests in workspace corresponding to the pipeline. To avoid keeping multiple copies of the project, i am planning to use Rest API to trigger Selenium tests and have configuration files in Git.
Is it possible to create multiple application properties files with custom properties, place them in Git and use required property file in a Spring Boot application(inside a Rest API impl) based on a property or RequestParam using Spring Cloud Config or something?
you can use spring boot profiles and pass it as maven argument
just pass -Dspring.profiles.active=test1 as command line argument
you can read more here
You can do it by setting spring.profiles.active environment variable. Like if you are using property file for every environment by convention application-dev.properties, application-qa.properties. You can define you spring.profiles.active as dev and qa.
You can refer to the below link for more in site on same.
https://stackabuse.com/spring-boot-configuring-properties/
You can use Spring cloud server and client.
Make A project With Dependency Spring cloud server and make config file(appilcation.properties) there. For use that config file in other microservice
just add spring cloud cilent dependency in other projects and add this line in application.properties
spring.cloud.config.uri:[your spring cloud server project url]
refrence:-
spring cloud server:-
https://www.youtube.com/watch?v=gb1i4WyWNK4&list=PLqq-6Pq4lTTaoaVoQVfRJPqvNTCjcTvJB&index=11
spring cloud client:- https://www.youtube.com/watch?v=E2HkL766VHs&list=PLqq-6Pq4lTTaoaVoQVfRJPqvNTCjcTvJB&index=12
Sharing the approach I ended up with as it might help someone someday.
As i wanted to create a Rest API and use properties from different config files in API implementation, based on the api request,
I created a Spring Cloud Config Server application that connects to application properties repo in git and I consumed Rest APIs exposed by Spring Cloud Config Server (host:port/app/profile) in the service layer of my Rest API implementation.

How to load changed environment variable in spring boot application running in PCF without restarting application?

We have a spring boot application running in PCF and it reads the PCF environment variables(CF_INSTANCE_INDEX, CF_INSTANCE_ADDR,..) from an application. Based on those variables, we are trying to implement the logic for a scheduler. While running this scheduler, these variables' values could have changed. Is there a way to refresh/reload bean that have env values during runtime?
we used #RefreshScope annotation on config properties bean.
#Configuration
#RefreshScope
public class PcfEnvProperties{
#Value("${CF_INSTANCE_INDEX}")
private int intanceIndex;
#Value("${CF_INSTANCE_ADDR}")
private String intanceAddr;
...
}
and refresh using
context.getBean(RefreshScope.class).refresh("PcfEnvProperties");
PcfEnvProperties pcfEnv = context.getBean(PcfEnvProperties.class);
But It is not loading the recently changed env variable into running application. Any ideas on how to accomplish this?
You can use Spring Cloud Config Server in combination with Spring Actuator to expose an endpoint in your service that will refresh the application's properties on the fly. You could set up your scheduler to hit this endpoint on a timer or as needed.
Here is one tutorial I found that seems pretty straightforward: https://jeroenbellen.com/manage-and-reload-spring-application-properties-on-the-fly/
You may have to play with the setup depending on how your platform is configured, but I believe it should do what you're wanting. We have deployed many java web services on our PCF platform using this actuator/config server approach, and we can just make a call to the refresh endpoint and it successfully pulls in (and overwrites when necessary) the new properties and values from the config server. Also you can pull out a list of the property names and values that changed from the response.
I'm not familiar with the specific property values you mentioned, but as long as they are normally a part of Spring's ApplicationContext (where properties usually are found) then you should be able to pull in changed values using this approach with Spring's cloud config server and actuator libraries.
Hope this helps

Is there a standard way to customize the deploy path in Spring Boot?

I'm exploring the possibilities of Spring Boot right now, and I'm at a slight impasse. I want to be able to run two Spring Boot applications at once, both on the same server, but at different paths (one deploys on /, the other deploys at /another-path).
Because this is an embedded Tomcat instance running within Spring Boot, there's no configuration files available for me to change.
Is there a standard way to do this? Is it possible?
Spring Boot comes with some pre-built property support. If you create an application.properties file, you can include:
server.port=<another port>
server.address=<another IP address>
server.sessionTimeout=<another timeout setting>
server.contextPath=/your-other-path
This can be in application.properties adjacent to your runnable JAR, embedded inside the JAR file, or simply applied as a -Dserver.contextPath=/your-alt-path with the java command. These are cascading, meaning you can embed one set of defaults inside the JAR, override with a local application.properties file, and then finally override application.properties with the -D options.
As it uses an embedded tomcat you should be able to add a /META-INF/context.xml to each application which specifies the path (at least this should work for a normal tomcat).
That works for our normal embedded tomcat stuff, so I would expect it to work for Spring Boot as well.

Categories

Resources