How to externalize Spring Boot application.properties to tomcat/lib folder - java

I need a configuration free, deployable war, myapp1.war that can retrieve the configuration files from the tomcat/lib folder.
As I have other web applications coexisting on the same Tomcat: myapp2.war, myapp3.war, I need this layout:
tomcat/lib/myapp1/application.properties
tomcat/lib/myapp2/application.properties
tomcat/lib/myapp3/application.properties
This way I can build the war files without any properties files inside the war and deploy on any server.
I have read the Spring documentation but it explains how to set the location when running as a jar:
java -jar myapp.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
I cannot figure out how to do this for the case of multiple coexisting war files.
I would like to know if this is possible or should I give up on Spring Boot and go back to the traditional Spring MVC applications.

A solution could be to load application-{profile}.properties as #PropertySource annotations as this question suggests, but then the logging system wont work, as you can see in the documentation.
The logging system is initialized early in the application lifecycle
and as such logging properties will not be found in property files
loaded via #PropertySource annotations.
This means that your logging properties in application-{profiles}.properties like:
logging.config=classpath:myapp1/logback.xml
logging.path = /path/to/logs
logging.file = myapp1.log
will be ignored and the logging system wont work.
To solve this I have used the SpringApplicationBuilder.properties() method to load properties at the beginning, when the application is configured. There I set the 'spring.config.location' used by Spring Boot to load all the application-{profiles}.properties:
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder) {
return springApplicationBuilder
.sources(Application.class)
.properties(getProperties());
}
public static void main(String[] args) {
SpringApplicationBuilder springApplicationBuilder = new SpringApplicationBuilder(Application.class)
.sources(Application.class)
.properties(getProperties())
.run(args);
}
static Properties getProperties() {
Properties props = new Properties();
props.put("spring.config.location", "classpath:myapp1/");
return props;
}
}
Then I have moved the properties files from src/main/resources to src/main/resources/myapp1
.
├src
| └main
| └resources
| └myapp1
| └application.properties
| └application-development.properties
| └logback.xml
└─pom.xml
In the pom.xml I have to set the scope of embedded tomcat libraries as "provided".
Also, to exclude all properties files in src/main/resources/myapp1 from the final war and generate a configuration free, deployable war:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<packagingExcludes>
**/myapp1/
</packagingExcludes>
</configuration>
</plugin>
Then in Tomcat I have
├apache-tomcat-7.0.59
└lib
├─myapp1
| └application.properties
| └logback.xml
└─myapp2
└application.properties
└logback.xml
Now I can generate the configuration free war and drop it into the apache-tomcat-7.0.59/webapps folder. Properties files will be resolved using the classpath, independently for each webapp:
apache-tomcat-7.0.59/lib/myapp1
apache-tomcat-7.0.59/lib/myapp2
apache-tomcat-7.0.59/lib/myapp3

With Spring 4.2 and #Annotation config and tomcat on linux serveur
In your Application class set the #PropertySource like that :
#Configuration
#EnableWebMvc
#PropertySource(value = { "classpath:application-yourapp.properties"})
#ComponentScan(basePackages = "com.yourapp")
public class YourAppWebConfiguration extends WebMvcConfigurerAdapter {
...
}
Now you just need to include the property file in your classpath
In production
Deploy your .war files ( or anything ) on tomcat, and put your application-yourapp.properties anyway on your production machine. ( for exemple in /opt/applyconfigfolder/application-yourapp.properties" )
Then in your tomcat ( here tomcat 7 ) open bin\catalina.sh
You have this line
# Ensure that any user defined CLASSPATH variables are not used on startup,
# but allow them to be specified in setenv.sh, in rare case when it is needed.
CLASSPATH=
Just add the path of the folder which contains application.properties
CLASSPATH=:/opt/applyconfigfolder
If you have already some classpath define you can add it
CLASSPATH=:/opt/applyconfigfolder:/yourpath1:/yourpath2:
I haven't try with windows but I think there is no problem
In Dev ( with eclipse )
├src
| └main
| └ ....
└config
| └application-yourapp.properties
instead of src/main/resources/application-yourapp.properties
Now in eclipse add your config folder to classpath, go to "Run Configurations" of your tomcat server ( or equivalent ) and add the folder Config to User Entries
Ok that's it, your application.properties is out of the application and your project run perfectly in dev environment.

Daniel Mora gave a good solution but instead of using spring.config.location you can use spring.config.name (https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files), so you can have different properties file for different web apps in the same tomcat/lib directory:
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder) {
return springApplicationBuilder
.sources(Application.class)
.properties(getProperties());
}
public static void main(String[] args) {
SpringApplicationBuilder springApplicationBuilder = new SpringApplicationBuilder(Application.class)
.sources(Application.class)
.properties(getProperties())
.run(args);
}
static Properties getProperties() {
Properties props = new Properties();
props.put("spring.config.name", "myapp1");
return props;
}
}
I think that the lib directory is for third party libraries not for storing configuration properties for your web apps.
So I think that a better solution is to add an external folder as additional classpath folder using shared.loader property in conf/catalina.properties:
shared.loader=${catalina.base}/shared/configurations
You can put your application properties app1.properties, app2.properties, ecc.. in apache-tomcat-7.0.59/shared/configurations.
Before finding Daniel Mora solution of overridding configure method of SpringBootServletInitializer my solution was to add a context.xml in src/main/webapp/META-INF with this content:
<Context>
<Environment name="spring.config.name" value="myapp1" type="java.lang.String" override="false" description="define the property file for srping boot application"/>
</Context>

Related

Is there is a way to make a spring boot app to be able to see a config folder in Resources and get its config whenever it needs?

I have a config folder of xml files that my spring boot app needs , its located on the same level of src folder ... Now i now to locate it in Resources Folder on the same level of application.properties.. There is any way to be able to do that ?
There many way to read your file .properties:
You can read with plain java like this:
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName));
you can use the #PropertySource annotation in a Spring #Configuration class:
#Configuration
#PropertySource("db.properties")
public class ApplicationConfig {
// more config ...
}
//Or if you your config outside of your classpath, you can use the file: prefix:
#PropertySource("file:/path/to/application.properties")
Using Multiple file :
#PropertySources({
#PropertySource("classpath:foo.properties"),
#PropertySource("classpath:bar.properties")
})
public class PropertiesWithJavaConfig {
//...
}
And so on ...

How to set the logfile name of Spring application and log to tomcat /logs folder?

How can I set the logging path relative to tomcat dir /logs/mylog.log?
What I tried: changing the logging.file property in application.properties
leaving the filename out: #logging.file= -> everything is logged to console, thus written into tomcat/logs/localhost.yyyy-mm-dd.log
logging.file=mylog.log -> written to console, thus same as #logging.file
logging.file=d:/mylog.log -> written to the location d:/mylog.log
logging.file=../logs/mylog.log -> written to console, thus still to localhost*.log
None was successful.
I'm not interested in externalising the configuration eg by providing system or environment variables.
I just created a simple Spring-bootapp from spring starter build as war file. I have just this modification in #SpringBootApplication class:
#SpringBootApplication
public class LogApplication {
private static final Logger logger = Logger.getLogger(LogApplication.class);
public static void main(String[] args) {
SpringApplication.run(LogApplication.class, args);
}
#Controller
#ResponseBody
public static class IndexController{
#RequestMapping("/")
public String getindex(){
logger.error("Error Logging");
return "Hello";
}
}
}
And this property in application.properties:
logging.file=../logs/mylog.log
Build the application using maven mvn clean install and put the war file inside webapps folder of tomcat. Started tomcat using startup.bat and hit successful the endpoint http://localhost:8080/demo-0.0.1-SNAPSHOT.
And the log was written in logs/mylog.log:
2017-01-04 14:57:10.755 ERROR 8236 --- [http-apr-8080-exec-4] com.example.LogApplication : Error Logging
You can make use of the environment variable for configuring the log path.
Tomcat sets a catalina.home system property which you can use
log4j.rootCategory=DEBUG,errorfile
log4j.appender.errorfile.File=${catalina.home}/logs/LogFilename.log
Note:-
This may not work On Debian (including Ubuntu), ${catalina.home} will not work because that points at /usr/share/tomcat6 which has no link to /var/log/tomcat6. Here just use ${catalina.base}. Check this link
I'm going to second Tomz's response and point you to the docs because they show you how to switch over from logback to log4j which is probably easier for you.
I would strongly recommend not deploying Spring Boot in war files, but as executable fat jars. It makes things a lot easier when you can just type this to test a configuration and deploy it:
java -jar my-service.jar /opt/my-service/conf/application.yml

How to convert a .war file into a JAR file?

I did a project based on J2EE framework. Using spring/ hibernate technologies. And used Tomcat as my servlet container. I want to create a JAR file and make my web project executable. This question is asked before but answers are ambiguous.
Have a look at Spring Boot, this will help you create a jar that can be started with
java -jar <your-jar-file>
It would not need to get deployed into a servlet container, instead it will run with an embedded tomcat. You'd need to install a java runtime wherever you want to run it, though.
See this link for more information.
To convert war file to jar in the spring boot, you need to do 3 steps.
convert war to jar in the pom.xml
<packaging>jar</packaging>
extends SpringBootServletInitializer
override configure
public class Your_Application_Name extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder){
return springApplicationBuilder.sources(Your_Application_Name.class);
}
public static void main(String[] args) {
SpringApplication.run(Your_Application_Name.class, args);
}

#PropertySource in a Jar for an external file on the classpath

I'm trying to use the Spring framework's #PropertySource annotation in a Jar to load a properties file from outside the jar, but it's not finding the file.
I need the properties file to be external to the Jar so it can be edited. I don't know the exact location where the file will be, I figured I could just have it anywhere on the classpath.
I'm using the following annotation on my Config class.
#PropertySource('classpath:stc.properties')
And placed stc.properties in the same directory as the created Jar file. I tried specifying the classpath explicitly in the java command, but it still cannot find the file:
java -cp . -jar stc.jar
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: Failed to load bean class: com.example.stc.Config; nested exception is java.io.FileNotFoundException: class path resource [stc.properties] cannot be opened because it does not exist
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:162)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:299)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:243)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:254)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:94)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:609)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
[...]
Etc.
I've also tried using ./ as the classpath, and tried specifying the classpath (with both variants) in the Class-Path attribute of the jar's manifest, but it always gives the same results.
Assuming you have two files, one for local one for production
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource(value = "${ws.properties}", ignoreResourceNotFound = true)
})
And in tomcat or your jar file , pass on this parameter
-Dws.properties=file:/path-to.properties
I added this in setenv.sh
APPLICATION_OPTS="-Dlog4j.configurationFile=file:$PATH/log4j2.xml -Dlog4j.debug=true -Dapplication.properties=file:$PATH/application.properties
This is possible with Spring 4 only
Use a variable (System or Environment) to have the value of the file and you could refer your file like this:
#PropertySource("file:${MY_PATH}/application.properties")
My environment was:
OS: Windows | Container: Tomcat | Java: 7 | Spring: 4.2.4 | Springboot 1.3.1 | Maven
Step 1 a (war):
Add the file externalised properties file to JVM system properties.
As am running this off tomcat; I done this by creating setenv.bat in <TOMCAT_HOME>/bin/setenv.bat
set CATALINA_OPTS=%CATALINA_OPTS% -Dexternal.app.properties=file:<PATH_TO_EXTERNAL_FILE>\application-prod.properties
Step 1 b (jar):
Alternative if you are running from a jar use:
-Dexternal.app.properties=file:<PATH_TO_EXTERNAL_FILE>\application-prod.properties
Note the use of file: at the start on the line.
Step 2: In my application startup class I used annotation #PropertySource to load the specific environment application properties.
#SpringBootApplication
#PropertySources({
#PropertySource(value = "${external.app.properties.file}", ignoreResourceNotFound = true),
#PropertySource(value = "classpath:application.properties")
})
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
Step 3:
Using externalised properties in project
external/file/path/application-prod.properties
spring.datasource.url.ext=< PRODUCTION_DATASOURCE >
/src/main/resources/application.properties
spring.datasource.url=${spring.datasource.url.ext}
Hope this helps other having the same problem.
try giving the full path of the file:
#PropertySource('file:c:/.../stc.properties')
you could use --spring.config.location=file:/somepath parameter when running jar, where you specify path to config file (could be relative).
More info in docs
Let's say you have a jar with a default main and some default stc.properties inside the classpath.
An option would be that if there is a configuration file with that name next to the jar (actually in the execution directory) the properties that are set inside that config are merged with the ones of default config.
If the user decides not to use the external config, no error occurs.
For the above scenario you need:
#PropertySources({
#PropertySource("classpath:stc.properties"),
#PropertySource(value = "file:./stc.properties", ignoreResourceNotFound = true)
})
e.g. let's say the default stc.properties (inside the jar) content is:
propA=valueA
propB=valueB
Now if I add a file with the same name next to the jar containing:
propB=updatedValueB
propC=valueC
The effective loaded properties when executing java -jar stc.jar are:
propA=valueA
propB=updatedValueB
propC=valueC

External configuration for spring-boot application

I have a spring-boot application which I want to run with external configuration file.
When I run it as jar (with embedded servlet container), everything is fine.
But I want to run it under external servlet container (Tomcat) and here i have problem with external configuration. I have tried a #PropertySource, but in this case application gets only properties absent in war file configuration: external configuration doesn't override internal configuration.
So the question: how can I configure external configuration which will override internal configuration?
You're probably using external configuration in the form of application.properties in the current directory when you're running your application as a jar. However, "current directory" isn't very useful when deploying as a war in an external tomcat. Even if you find out what the current directory is, it's most likely the same location for all applications running in that tomcat, so when you're running more than one application, that's not going to work very well.
What we do here is this declare two PropertySources on our application:
#PropertySources({#PropertySource(value={"classpath:internal.properties"}), #PropertySource(value={"file:${application.properties}"})})
internal.properties contains "built in" default values for propeties. The second PropertySource is a file containing external configuration. Note how the name of the file is itself a property.
We define this externally in the Context element of our application (in tomcat):
<Context docBase="/path/to/your/war/your.war">
<Parameter name="application.properties" value="/path/to/your/properties/application.properties"/>
</Context>
This allows you to have multiple applications running in tomcat, each application using it's own external properties file. You can even have multiple instances of the same application running with different properties.
Spring Boot offer many ways to specify the location of your properties, it´s not needed to modify your sources.
Yo can define the spring.config.location value for example:
In your tomcat/conf/Catalina/<host> context descriptors:
<Context>
<Parameter name="spring.config.location" value="/path/to/application.properties" />
</Context>
As a JVM parameter in your tomcat setenv.sh file:
-Dspring.config.location=/path/to/application.properties
As a SPRING_CONFIG_LOCATION environment variable.
To externalize the Spring Boot application.properties when deploying as a war file you can set spring.config.location at the beginning when Spring Boot application is configured:
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder) {
return springApplicationBuilder
.sources(Application.class)
.properties(getProperties());
}
public static void main(String[] args) {
SpringApplicationBuilder springApplicationBuilder = new SpringApplicationBuilder(Application.class)
.sources(Application.class)
.properties(getProperties())
.run(args);
}
static Properties getProperties() {
Properties props = new Properties();
props.put("spring.config.location", "classpath:myapp1/");
return props;
}
For more details check this solution.
You can add configuration files folder to set Classpath line catalina.bat, catalina.sh(which one if you want to use.) or you can add to setenv.bat/sh file. Your config files will be added to war classpath.
For Example;
In Windows env.
set CLASSPATH=D:\app\conf

Categories