Using application.properties to set jvm arguments - java

I'm using spring boot 2.6.2 with docker etc. - my app reads some configuration via application.properties which looks like this:
foo.bar=hello
run.jvmArguments=-Xmx1G -XX:+ExitOnOutOfMemoryError
foo.bar definitely works as expected. But I'm not sure if it's correct to put ..
run.jvmArguments=-Xmx1G -XX:+ExitOnOutOfMemoryError
.. in there too. Does this work?
Plus - I'm using DefaultPropertiesPersister from spring to manage and change some variables in application.properties which works like a charm. But for some reason it puts some backslashes in there which results in:
run.jvmArguments=-Xmx1G -XX\:+ExitOnOutOfMemoryError
.. is this still correct? Does it work?
Thanks for any help or advice :-)

Properties from application.properties are loaded after the JVM is started and before the application context is initialized.
So there isn't any chance of them affecting the JVM.
Also there is not any real relation between application properties and environment properties for JVM. Not every spring application is a spring boot application that loads a JVM for the embedded Server. Some spring applications are deployed as wars without an embedded server, where the JVM already executes and it is the hosting server, for mutliple applications (meaning probably multiple application.properties).
Also take a look on Spring doc
SpringApplication will load properties from application.properties
files in the following locations and add them to the Spring
Environment
Also here
All configuration in Spring emanates from the Spring Environment
abstraction. The Environment is sort of like a dictionary - a map with
keys and values. Environment is just an interface through which we can
ask questions about, you know, the Environment. The abstraction lives
in Spring Framework and was introduced in Spring 3, more than a decade
ago.
Spring environment is not the same as OS or System environment that affects the JVM.

Related

How to configure Spring Boot datasource part to use in production?

I'm writing my first "real" applicaion in my first job. I could deploy my app using Spring Boot and it works just fine. One thing that i doubt is datasource config part. Now i write all datasource config in application.properties file:
spring.datasource.url = jdbc:postgresql://10.60.6.34:5432/postgres
spring.datasource.username = *username*
spring.datasource.password = *password*
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQL94Dialect
And that's it! What else should i do to make my app production ready? What about connection pooling and all that stuff? (i'm not quite familiar with all that datasource config stuff) Thanks in advance!
So Joe W is not wrong - profiles is an OK way to handle this issue. However, what I'd recommend instead is to handle the issue using environment variables. This will make your application compatible across not only all operating systems (which profiles will too), but will also allow you to run it within Docker (containers) more easily. You will need to do some amount of this anyway, since profiles still requires you to specify which profile you're running, which you'll need to do with an environment variable.
Luckily for you, Spring Boot auto-wires environment variables with no extra work on your side. You can read more about this here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
When dealing with environment variables, you use underscores instead of periods, so your configs would look like this:
SPRING_DATASOURCE_URL = jdbc:postgresql://10.60.6.34:5432/postgres
SPRING_DATASOURCE_USERNAME = *username*
SPRING_DATASOURCE_PASSWORD = *password*
SPRING_JPA_HIBERNATE_DDL_AUTO = update
SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT = org.hibernate.dialect.PostgreSQL94Dialect
Then you can set your environment variables to whatever you want, and you don't need to worry about pulling in new profiles for each server. In addition, since environment variables are higher in the hierarchy than file-based configurations, you can leave your current file-based configurations alone (if you'd like) and your environment variables will override them when you deploy.
Around your connection pooling, this is going to heavily depend on your backing servlet container (I.e., tomcat vs other) and your backing database (looks like postgres). I'd recommend you look at tomcat-jdbc usage with Spring boot, which will then allow you to configure the things like max connection pools and such within Spring's environment variables as well.
You should take advantage of SpringBoot profiles that will allow you to define separate configuration for dev, stage, prod, and any other environment you want based on a property.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
A profile gives you a way to control which configuration is loaded based on a property that defines where the application is deployed.
Your question on data connection pooling depends on your backing data storage selection and how that particular storage is setup. In general, when you go to production you should be using connection pooling of some kind but how much and what kind depend on your implementation.
Additionally, you can use spring config service . Config-service is central location(which can be more secure) for all properties and with very minimal configuration/change your spring-boot application can read properties from config-service.
You should provide your database parameters as environment variables then set them in your application.properties as placeholders. For example:
spring.datasource.url=${DATASOURCE_URL}
Where DATASOURCE_URL is one of the env. variable.
In your IDE you set them in the project setting (for example)
So at your work you set your local parameters in IDE, and on the production machine you set prod parameters as environment variable.

How can I get Spring Boot to load the Java System.getenv() variables into the Environment object?

Azure loads startup details into the System.getenv() properties. I would like them to show up in the Environment variable from Spring.
How can I accomplish this?
As a side note, is there a way to add a system variable at startup (for testing?)
you can add all your properties to application.properties in Spring Boot. otherwise, you can add a Bean in your spring boot application class that reads from the system properties & loads them.
The actual answer to this question is "nothing". Spring boot automatically loads all the System.getenv() into the Environment variable at startup.
See: Spring Boot docs
And yes, I am a bit embarrassed.

application configuration for java apps in kubernetes

I'm new to java and k8, and I have some doubts about how to handle application configurations for my java apps. I've got one spring boot app and the other three use wildfly.
So, they all got hardcoded application configurations, and when starting them the just use something like:
java -Dswarm.project.stage=development -jar foobar/target/foobar-swarm.jar
except for the spring boot which has an application.properties file that consists of application configuration data.
So basically the three java apps have backed in two files (which I know is a no no):
- project-stages.yml
- standalone.xml
And when the developer wants to deploy to production he uses:
java -Dswarm.project.stage=production -jar foobar/target/foobar-swarm.jar
And, now we come to kubernetes which has three ways of dealing with application configuration data:
1.) Env variables
2.) Config maps
3.) Secrets
I was thinking of using configmaps instead of env variables because they have more benefits.
So, the developer gave me the possibility of overwriting those hardcoded variables with an external file : Dsystem.properties.file=/var/foobar/environment.properties
But I'm still overwriting an hardcoded files with an external file, and I'm not happy with that solution!
So, I'm basically looking on advise can those hardcoded files be supplied externally and populated with configmaps in k8 - what would be the best practice of handling the config files in the world of k8?
Tnx,
Tom
There are several questions in the post, but I can address only the one related to spring-boot.
The simplest and the most convenient way of specifying configurations to spring boot app is via its built in profiling feature. As you already mentioned you have application.properties. You can create similar files according to your usage cases: application-production.properties, application-staging.properties, application-k8s.properties, etc.
Kubernetes deployment doesn't change this in any way.
You can control which configuration to pick by setting SPRING_PROFILES_ACTIVE env variable from the kubernetes.
You might have something like this:
docker run -e SPRING_PROFILES_ACTIVE=k8s -d -p 0.0.0.0:8080:8080 \
--name=yourapp your_image_name bash -c "java -jar yourapp.jar"
It will pick configuration from application-k8s.properties.
Configuration files support environment variables as well.
You can have placeholders like ${YOUR_DB} in your properties files and Spring will automatically pick up env variable with name YOUR_DB. It is convenient to use this feature let's say when your app pod must have its own db pod.
If I got your question right you are asking how to configure a Spring Boot application via a k8s ConfigMap. Yes, you can do that.
Create a Docker image with WORKDIR work_dir in which you start the Spring Boot application eg via java -jar /work_dir/app.jar
Create a ConfigMap
Run a container of the above mentioned image within k8s
Mount the ConfigMap for the Spring Boot application.properties into the Container as /work_dir/config/application.properties
On changes in the ConfigMap the file within the container gets updated. You have to restart the Spring Boot Application to set your changes active.

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.

How to implement environment-specific init parameters in my tomcat application

I'd like to be able to implement a configuration-less deployment for my java application (tomcat7, spring-mvc). For example, right now we are considering creating one context.xml file for each environment (prod, stage, dev) we deploy to:
context_prod.xml
context_stage.xml
context_dev.xml
context.xml (for localhost)
On each of our servers we would have a symlink context.xml which would point to the appropriate context file (e.g. context_prod.xml). So when we deploy, we don't have to worry about changing database references, keys, etc.
I feel like there's probably a better way to do this; perhaps one that is built into spring?
Spring has recently added the functionality to handle environment configuration:
http://blog.springsource.com/2011/02/11/spring-framework-3-1-m1-released/
This still seems a little bit complicated for me and I have done exactly what you are asking in my own Spring MVC applications for our logging. In my DispatcherServlet configuation I have this line:
<context:property-placeholder location="classpath*:/system.properties"/>
<util:properties id="mySystemProperties" location="classpath:/logging/log4j-${system.runMode}.properties" />
system.runMode is just an env variable that we created and is set in CATALINA.SH at startup like this: Setting environment variable TESSDATA_PREFIX in Tomcat
I then use Spring EL to reference any values I want and it works per environment. As you can see I did this for our logging and have a different logging configuration file per environment.
You could use Spring #Profile introduced in Spring 3.1. In your case you could use profiles like dev, stage, prod etc.
This profile value could be initialized run time. So when your application started, Spring could fetch appropriate profile based on configuration.
You could setup profile from environment variable, via deployment descriptor (web.xml) etc. This Spring source tutorial could be interesting for you.
I personally using Maven build to replace the profile value during build time in the web.xml. In the build time I passed profile value as build argument.

Categories

Resources