Where to place a configuration file outside application war - java

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,

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 spring mvc application scan configuration files under classpath

Here i have a bean initialized InitializingBean in Spring MVC application. In order to register in spring-servlet application context, i two lines of code below:
#Autowired
private ApplicationContext context;
#Override
public void afterPropertiesSet() throws Exception {
if (context.getParent() != null) { // debug mode and add a breakpoint here, four times came here while application start up
}
}
Obviously, if four root application context initialized, some where error. but i really don't know why?
Here i make some assumption:
contextConfigLocation in web.xml file is classpath*:, and tomcat may detect multiple applicationContext.xml files under classpath jar files.(but spring xml files are really not locate in such jar files)
there are some configuration class in jar files and who make spring mvc application scan the same jar file twice or more(but i did not find such class)
Maybe i'm totally error, hope someone can give me some hint and thanks.
when starting your tomcat,the tags in your web.xml ,specificly,your codes will detect the configuration you wrote,initilized the spring container,such as,Application-Context.xml,if there is any tag in this file , the Spring will add the file you write, for example, you write "Application-context-data.xml" in your tag,the Spring container will initilized it !

Can I define System Properties within Spring Boot configuration files?

I have a single application.yml configuration file for my Spring Boot app that defines two profiles (as described in the documentation).
When the production profile is enabled, I would like to set the http.maxConnections system property to a custom value, e.g.
spring:
profiles:
active: dev
---
spring:
profiles: dev
---
spring:
profiles: production
http:
maxConnections: 15
But this doesn't actually set the system level property; it appears to just create an application-level property. I've verified this through both http://locahost:8080/env and a JMX Console when comparing launching by
java -jar -Dspring.profiles.active=production myapp.jar
versus
java -Dhttp.maxConnections=15 myapp.jar
I suppose I could create a bean that's #Conditional on the "production" profile that programmatically callsSystem.setProperty based on my application.yml-defined property, but is there a simpler way through configuration files alone?
You may try.
#Profile("production")
#Component
public class ProductionPropertySetter {
#PostConstruct
public void setProperty() {
System.setProperty("http.maxConnections", "15");
}
}
I suppose I could create a bean that's #Conditional on the "production" profile that programmatically callsSystem.setProperty based on my application.yml-defined property, but is there a simpler way through configuration files alone?
I think that's your best bet here. Spring Boot does that itself in its LoggingSystem where various logging.* properties are mapped to System properties.
Note that you'll probably want to set the system properties as early as possible, probably as soon as the Environment is prepared. To do so, you can use an ApplicationListener that listens for the ApplicationEnvironmentPreparedEvent. Your ApplicationListener implementation should be registered via an entry in spring.factories.
You can inject the environment into the constructor of the class that specifies the beans. This allows you to write application properties to the system properties before the beans are being created.
#Configuration
public class ApplicationBeans {
#Autowired
public ApplicationBeans(Environment environment) {
// set system properties before the beans are being created.
String property = "com.application.property";
System.getProperties().setProperty(property, environment.getProperty(property));
}
/**
* Bean that depends on the system properties
*/
#Bean
public SomeBean someBean() {
return new SomeBean();
}
}
You can also use PropertyPlaceholderConfigurer from org.springframework.beans.factory.config to get handle over your properties file

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

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