Changing Spring configuration file? - java

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).

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 :

How to have a datasource configuration as external Jar in spring boot

I want to create a spring boot JAR(standalone) which contains Java data source configuration and I want to utilize that JAR in other micro services for connecting to DB (instead of giving credentials in each application.properties file). I have succeeded in creating the JAR with java data source configuration. But when I add that jar to any service, the service is still expecting me to provide credentials in application.properties.
Could anyone help on how to have data source configuration in an external JAR and use that jar in service for db connection
You could implement a single properties file to take the data source credentials from there (you don't necessarily have to use Spring Boot's default application.properties) and then the easiest way to get the connection is by defining a DataSource factory method and placing it inside a class annotated with the #Configuration annotation:
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
}
This manually generates the connection to the data source that you are indicating, just make sure that all the necessary libraries for the connection are being imported correctly in your pom.xml file if you are using Maven.

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

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,

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