External configuration for spring-boot application - java

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

Related

Get values of individual context.xml in Jersey's ResourceConfig?

I have a pretty simple servlet setup with
Jersey
no web.xml
Tomcat 9
Maven for creating the .war and handling dependencies
Now I need to deploy a test and a production version of the servlet on the server and I a trying to use the individual context.xml file for each environment. A quote from the docs
Individual Context elements may be explicitly defined:
In individual files (with a ".xml" extension) in the $CATALINA_BASE/conf/[enginename]/[hostname]/ directory. The context path and version will be derived from the base name of the file (the file name less the .xml extension). This file will always take precedence over any context.xml file packaged in the web application's META-INF directory.
All this also sounds easy here:
To give an example: if we wanted to deploy three installations of an application for test, stage and production, we would create three context.xml files:
tomcat/conf/catalina/localhost/test.xml
tomcat/conf/catalina/localhost/stage.xmltomcat/conf/catalina/localhost/prod.xml
And then deploy the same .war file three times as:
tomcat/webapps/test.war
tomcat/webapps/stage.war
tomcat/webapps/prod.war
And each installation would pick up its specific configuration automatically.
You can also read this documentation:
For Tomcat 5.0.x and later, WTP 2.0.x and later offers the opportunity to write the contexts to separate XML files in the proper folder (typically conf/Catalina/localhost) according to the requirements of that particular version. This behavior is enabled by checking the Publish module contexts to separate XML files option in the Server Options section of the Tomcat server editor. Note that only contexts for added projects will be written to separate XML files. Manually added contexts in server.xml will remain there.
There are several instructions, how to retrieve a value from the context.xml. For example:
<Environment name="companyName" value="My Company, Incorporated" type="java.lang.String" />
Can be used by
InitialContext context = new InitialContext();
Context xmlNode = (Context) context.lookup("java:comp/env");
String companyName = (String) xmlNode.lookup("companyName");
But this was listed for a Spring setup, how can this be done in a Jersey ResourceConfig based application/servlet?
For example:
#ApplicationPath("/")
public class MyMain extends ResourceConfig {
private final Logger logger = LoggerFactory.getLogger(MyMain.class);
public MyMain() {
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
String myEnv = (String) envCtx.lookup("my-env");
this.logger.debug("Env: {}", myEnv);
} ...
is running into NamedExceptions: javax.naming.NameNotFoundException: Name [my-env] is not bound in this Context. Unable to find [my-env].
Is there any way to get the context configuration with Jersey, or do I need to use a different approach?
I also have no clue, how to debug the InitialContext. So the file is there and it is read by Tomcat, but I don't know how I can access it in the application. Do I need to use ServletContext.getInitParameter() instead - and how?
Update
My Eclipse setup seems to be the problem, because the published xml file is not the original, individual context.xml in my /Catalina/localhost folder. Is there any way to make sure that the original file is published in the Eclipse-Tomcat server?
"Publish module contexts to separate XML files" is checked. What is Update context paths? in publishing options (no effect visible, though)?
Here is the :

Where to place a configuration file outside application war

I have a Spring application that at startup needs to read some basic properties from a file but nothing sensitive (timeout values, directory locations, etc.). It needs to be edited before starting the application depending on the desired target server.
My first easy idea was to place the file under home target server, then load it into Spring
<context:property-placeholder location="file:${JBOSS_HOME}/standalone/config/application.properties" />.
I found other sources mentioning the usage of System properties or JBoss modules.
Are there any advantages/disadvantages for using one on another? What else should I consider when choosing the appropriate one in my case?
In my understanding Spring looks by default properties file in your app directory, or you can register a properties file with XML config or Java config (Annotations):
<context:property-placeholder location="classpath*:my.properties"/>
Then you refer to the properties in your beans:
#Component
class MyClass {
#Value("${my.property.name}")
private String[] myValues;
}
or
#Configuration
#PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
#Autowired
Environment env;
#Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
the properties file can be updated at any time (just stoping serv) in the apps server, tomcat, jboss, etc.
note sure if that's what you want,

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

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>

How to enable directory listing in Spring 4 with Tomcat?

I know there is are global Tomcat settings, but is there an approach to define directory listing on application (context) level? I'd like to list static content from the directory, e.g. "webapp/images".
I do not have web.xml. The configuration is done in WebAppIntializer which extends AbstractAnnotationConfigDispatcherServletInitializer and everything is by default.

Changing Spring configuration file?

I have a class that reads some properties from this application.properties and add them as configuration properties to the java/spring application. What I am trying to achieve now is this: I do deliver the package to the client, and inside this application.properties there are database connection details, so the client can change them. The question is, can this be done in the stage after we create the .war file. So basically the client goes and changes the configuration file, and on deploy these configs will be used, or that must be done prior building the .war file. Attached you will see my solution:
//application.properties
#DB properties:
db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
db.url=jdbc:sqlserver://ip\\instance:port;databaseName=db_name
db.username=db_username
db.password=db_pass
//WebConfig.java
#Configuration
#PropertySource("classpath:application.properties")
public class WebAppConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
#Resource
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
The usual way to do this is with JNDI. It's a directory (lookup) service specifically designed for providing database connections, properties, and other runtime configuration data to packaged Java EE applications.
You can put your connection information in your server (tomcat, jboss or other). There are specific files to do that (in tomcat it is the context.xml file for example).
After, you can use this information in your application using datasource. So, when the war file is deployed on the server, the connection information are known.
Instead of packaging up application.properties, why not have it as an externally referenced file, e.g.:
#PropertySource("file:/external/path/to/application.properties")
This would allow clients to change the file without modifying your packaged distribution.
Common classpath way
In a most servlet containers there is common folder where you can store classpath resources available for all web applications. You can use this folder for configuration purposes. Change your build process to do not include application.properties into the final war file. You can prepare for example another maven project for such config files aside. Provide a copy of application.properties in your deployment documentation. During deployment you need to adjust properties in provided application.properties file according to your environment and then place it in a folder that is common for all webapplications. For Tomcat it is $CATALINA_HOME/lib folder. During development you can add a folder with this property file to server classpath via launch configuration. As a downside you can have naming conflicts between two webapps that use the same approach (two application.properties files).

Categories