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

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.

Related

Define environment variable for app in Tomcat, outside of deployment

I need to provide a config file path to my Java web application that runs in Apache Tomcat 10.1.
The config path must be set outside of the deployment. It's a property of the machine / stage on which the application is deployed. And not an attribute of the application. So it must not be set in the web.xml.
For example /srv/myapp/prod/config.ini or C:\EclipseDeveloperOnWindows\config.ini.
I've thought about multiple options:
Operating system environment variable MYAPPCONFIG, like PATH or JAVA_HOME: Independent of my application and even independent of the Tomcat container. Downside: May become error-prone if I want to run multiple distributions / Tomcats with different configurations as the same system user.
Java system property as a command line option for Tomcat in CATALINA_OPTS, e.g. -DmyApp.config=/some/path.ini
Tomcat context parameter in <CATALINA_BASE>\conf\server.xml. This way, each Tomcat instance (one CATALINA_HOME, multiple CATALINA_BASE) could define a different value for the config path property. (Do I have to include a reference to the server.xml context parameter in the web.xml? And can I look it up from ServletContext?)
Tomcat environment entry in <CATALINA_BASE>\conf\server.xml - what are the differences / pros and cons of Tomcat context parameters vs. Tomcat environment entries? (Can I look it up from ServletContext?)
Something else, that maybe can be looked up using JNDI?
Did I miss some options? And which one is recommended? Which approach do you use for what reason?
Putting the configuration into the Context is probably your best option. Note that Contexts can be defined in various ways, not just in server.xml.
See the Defining a context in the Tomcat 10.1 docs for details.
For our product, the same war file is deployed to the staging environment and the production environment. The differences between the environments are exhaustively described by the two Context descriptors (one for each environment).

Using application.properties to set jvm arguments

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.

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.

Production vs staging - adding staging specific controllers

We are using spring with a tomcat 7 cluster for our web application.
In order to do an load testing our staging server we need to have some sort of a "special controller".
This controller is providing some really risky operations that must not reach the production server!
What is the best way to do this? how can we create this code, add it to our svn, and still make it unavailable to production deployment?
Thanks
I'd suggest using Spring profile. You can set a bean to load only when a specific profile is active. This way this controller would load only when profile staging is active.
For example you can define:
#Controller
#Profile("staging")
public class SpecialController {
...
}
You need to pass in the JVM options the profile -Dactive.profiles=staging
Spring has special feature for this named "profiles". You can define staging profile, create as many beans as you need marked for this profile. Then, when you are running application you just have to supply JVM option -Dspring.profiles.active=staging and all staging targeted beans will run. Otherwise they will be ignored.

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