Spring Boot: Load profile from dependent jar - spring

I have a Spring Boot application and a dependent common jar. This jar is for third party connections e.g. database, LDAP etc. And this common jar is going to be used in other Spring Boot services. There for I want to keep all the connection details in one place in the common jar so that when I use this common jar with other applications, I don't have to replicate the same settings repeatedly. My application structure is,
ServiceA
application-dev.properties
env=dev
application-prod.properties
env=prod
--------------------------
Common.jar
common-dev.properties
jdbc.url=
jdbc.un=un
jdbc.pass=pass
common-prod.properties
jdbc.url=
jdbc.un=un
jdbc.pass=pass
---------------------
When I start the application with an environment profile then I want the respective properties file from common jar is also loaded. Let's say I start the application as following,
-Dspring.profiles.active=dev
I want common-dev.properties also gets loaded from the common jar.
Thanks in advance.

Related

Log4j2 not loading from external classpath in an EAR

The project artifact structure is:
test.ear
--lib - has all log4j2 jars core, web, bridge and JCL
--META-INF -- has app.xml, JBoss specific deployment XML, and manifest file
--a.war -- each war has web.xml with log4jConfiguration and Log4jServletContextListener specified.
--b.war
--c.war
The war creates context with log4jConfiguration, which is "classpath:test.xml". I can see each war creating the context with log4jContextName I have provided in web.xml.I have a few crons, too, which run based on configured time intervals. When crons run, and many of the JMS process run, I observed the log files are not populated with logs(From project-specific classes). While going through the log4j2 code, I understood that log4j2 creates a context for each classloader. And in my case, it creates a context for "test.ear", which is defaulted to error(DefaultContext) as it is not able to find a default log4j2.xml since I have a custom named(test.xml) on in the classpath. The Log4jServletContextListener does not catch the "test.ear" event.
How to inject my "classpath:test.xml" while log4j2 creates a context for the ear file? Since my project can be both deployed to WebSphere and JBoss, I am looking forward to suggestions that are not server-specific. Or is there a way to create a single context for all the war and ear somehow? I have different apps outside this ear in same server so I cannot give an environmental config of -Dlog4j.configurationFile as other apps have there own log4j2 xml's.
One approach that I can think of is to place your log4j config file test.xml in a shared library and configure your application to use the shared library.
The following link describes how to configure a shared library for a server or an enterprise application on WebSphere.
https://www.ibm.com/support/pages/how-create-shared-library-and-associate-it-application-server-or-enterprise-application-websphere-application-server
The shared library should be a generic function for modern Java app servers, so JBoss should also be able to configure this.

Using Spring Boot profiles on external tomcat

I am looking for a solution to the problem, which is that having an external Tomcat server with several different applications (war),
I would like to use application Spring Profile (dev, prod) to choose application.properties for a given profile.
To avoid keeping the database configuration in the git repository, I didn't store in the application
application.properties, but I kept them in the $catalina.base directory, where in common.loader
I indicated the path to this directory. This worked until I started deploying several different applications. Then each of them began to use
from the same datasource. One solution is to keep the application.properties in Jenkins and directly in Jobie indicating which one to use,
but I am not sure if this is a good solution.
Option 1: Create a separate instance of tomcat (using same tomcat binary) and place application.properties in instance's classpath. As classpath is separate for every instance, applications don't refer to same application.properties file. Refer to this article to see how to create multiple instances on same tomcat server.
Option 2: Create JNDI datasource on tomcat server and use it in your application to access database. This way, should you choose to, same datasource can be shared across different related applications and/or modules. For more information on how to create JNDI datasource in Tomcat, refer to this link

Use External properties file

Am looking to pass variables at run time once war file is deployed on tomcat ..
How can i use application.properties whcih is in classplath along with another properties file ex. abcd.properties located at particular directory..Am basically looking to set additional classpath and read value from properties file in that path along with default classpath location for war deployment.
Am using Spring boot .One of the way is to pass all properties to database end , but am looking for a file based i.e properties based workout.
(Having multiple applications on same tomcat instance.)
Spring Boot App --> run as --> run configurations. Now here in VM arguments add Dproperties.location="Path of the properties".
Now, in your Spring Boot application use the annotation #PropertySource("file:${properties.location}/propertiesfileName.properties") just above the class declaration.
Autowire Environment in your class. use env.getProperty("propertyname").
You can access the values from application.properties as usual using #Value annotation. Hope this helps.

How to configure different data sources for local testing and deployment in Spring Boot Application

I am trying to find the best way to configure my Spring Boot Web application to easily switching between the following data sources for both local testing and deployment.
H2 in memory db. Local testing only.
Dev oracle. Local testing and deployment.
Prod oracle. Deployment only.
By local testing, I mean to test in IDE environment (Eclipse). Dev and prod oracle databases are set up on two remote servers.
After some research, there are different ways to switch from one data source to another.
Use Spring profile. Using H2 and Oracle with Spring Boot. Set up the following files in classpath, application.properties, application-h2. properties and application-dev.properties. While connections for h2 and dev are defined in corresponding properties files, spring.profiles.active is set in application.properties. My understanding is this property can be overridden during build process by specifying spring.profiles.active. However, it seems to be a JVM variable, how do I set it running maven?
Maven profile. Create multiple profiles in pom and a filter pointing to application properties files. The profile specified by -P option during maven build will determine which application properties file to look. However, according to maven application with multi environment configuration can't deploy on tomcat, this will generate multiple wars for different deployment. So method 1 is preferred. Plus, it does not apply to switching datasources while testing locally.
Persistence units. Define different persistence units for different data sources in persistence.xml. Use EntityManager by choosing a specific unit. Variation of this method include having a variable in unit names which is determined in application.properties.
JNDI lookup. Set up a jndi name in application.properties with spring.datasource.jndi-name. The actual database information including url and credentials will be specified in context.xml in the tomcat folder where the war will be deployed.
My mind is set on local testing environment. Gonna go with method 1. Switching between H2 in memory and oracle is so easy just by changing the property in application.properties. Since the testing is usually done in IDE, war does not need to be generated, although answers are welcome for run maven install with spring.profiles.active.
As far as deployment, JNDI is definitely the way to go. However, I am concerned that the two properties in application.properties: spring.profiles.active and spring.datasource.jndi-name may be conflicting with each other. If I have spring.profiles.active=h2 and then tried to deploy the war to prod server, does it try to connect to h2 based on the spring profile or to prod db based on jdni-name? What is the best practice to accommodate all scenarios with enough flexibility?
Also is a explicit configuration class for DataSource required such as Configure Mutiple DataSource in Spring Boot with JNDI? My understanding is application.properties and spring profile should be enough to handle it, right?
Definitely use Spring profiles.
You don't want to use Maven profiles as it creates different artifacts. Ask your QA/Release engineers how they feel about having different artifacts for different environments :). They wouldn't be happy.
H2 is what you want to use in CI server integration testing as well. Such integration testing is fast and easy.
Instead of changing profile in application.properties, consider defining profile via command line parameter. So that configuration file changes are not required to run your application in different profiles.

Spring boot application.properties maven multi-module projects

We are using spring boot in a multi-module project.
We have a Domain access module which has the common domain object classes, repositories, together with configuration for the datasource, JPA, Hibernate, etc. These are configured using a application.properties. We put all this configuration into the common module to save duplicating these common configurations in the higher level modules.
This all works fine when building the domain module, so the configurations are loaded correctly in the test units.
However the problems start when we try to use the domain module in the higher layer modules; they have their own application.properties which means Spring loads them and not the the Domain module application.properties, which this means the data source is not configured because only the higher module application.properties are loaded.
What we would like is both the domain module and higher level application properties to be loaded by Spring. But we can't see any easy way to do this.
I'm thinking this must be a common problem, and wonder if there any recommended solutions for this problem?
As we are using spring-boot the solution should ideally use annotations instead of applictionContext.xml.
Maybe you should only use application.properties in the top-level aggregator project?
You can always use #PropertySource in the child projects to configure them with a name that is specific to their use case.
Or you can use different names for each project and glue them together in the top-level project using spring.config.location (comma-separated).
I agree with #Dave Syer. The idea of splitting an application into multiple modules is that each of those is an independent unit, in this case a jar file. Theoretically you could split each of those jar files into their own source repositories, and then use them across multiple projects. Let's say you want to reuse these domain classes in both a web and batch application, if all the APPLICATION level configuration is stored within each of the individual modules, it severely reduces their reusability.
IMO only the aggregating module should contain all of the configuration necessary to run as an application, everything else is simply a dependency that can be remixed and reused as necessary.
Maybe another approach could be to define specific profiles for each module and use the application.properties file just to specify which profiles are active
using the spring.profiles.include property.
domain-module
- application.properties
- application-domain.properties
app-module
- application.properties
- application-app.properties
and into the application.properties file of app-module
spring.profiles.include=domain,app
Another thing you can do (besides only using application.properties at the top-level as Dave Syer mentions) is to name the properties file of the domain module something like domainConfig.properties.
That way you avoid the name clash with application.properties.
domainConfig.properties would contain all the data needed for the domain module to be able to tested on it's own. The integration with the rest of the code can easily be done either using multiple #PropertySource (one for domainConfig.properties and one for application.properties) or configuring a PropertySourcesPlaceholderConfigurer bean in your Java Config (check out this tutorial) that refers to all the needed property files
in spring-boot since 2.4 support spring.config.import
e.g
application.name=myapp
spring.config.import=developer.properties
# import from other module
spring.config.import=classpath:application-common.properties
or with spring.config.activate.on-profile
spring.config.activate.on-profile=prod
spring.config.import=prod.properties
ref: https://spring.io/blog/2020/08/14/config-file-processing-in-spring-boot-2-4

Resources