Pass values into spring from maven install -Denv=value, deploy on Tomcat - spring

I have a maven project that uses spring and I would like to insert a filename into Spring whose contents has various database information. The filename can either be test.properties, prod.properties or dev.properties. I have seen posted that one can type
"mvn install -DfileTarget=test.properties"
and have the system property be set when a user builds project. However I am getting the following error when I deploy the project on Tomcat 8.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'fileTarget' in string value "file:${jamesTarget}"
My properties (test, prod, dev) files contains DB related config values:
db.driver = jdbc:\\.....
db.url = url of database.
db.user = username
db.passwd = passwd
My Java code is as follows. Is there something I need to add into the pom.xml? It seems like when deploying on Tomcat, the system property is not found, how would I set this leaving the ability for me to install different files at build time?
Thanks in advance
#Configuration
#PropertySource({ "classpath:${fileTarget}" })
public class DbConfig {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
System.out.println("driver : " + env.getProperty("db.driver"));
System.out.println("driver : " + env.getProperty("db.driver"));
System.out.println("user : " + env.getProperty("db.user"));
System.out.println("passwd : " + env.getProperty("db.passwd"));
}
...

Related

Spring boot: external properties file an PropertySource

I am developing a spring boot application with gradle.
I would love to tell spring where to read .properties files with a parameter. Since I am still running it via gradle, I have added this to my build.gradle
bootRun {
args = [
"--spring.config.additional-location=file:/path/to/my/props/folder/,file:/path/to/another/props/folder/"
]
}
into /path/to/my/props/folder/ I have created a file remote-connection.properties:
### remote-connection
remote.ip.address=127.0.0.1
remote.ip.port=5001
and I am trying to load those props like this
#RestController
#PropertySource("file:remote-connection.properties")
public class MyController {
#Value("${remote.ip.address}")
private String remoteIpAddress;
}
When i run ./gradlew bootRun i have the following error
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [my.package.MyApplication]; nested exception is java.io.FileNotFoundException: remote-connection.properties (No such file or directory)
(I have also tried #PropertySource("classpath:remote-connection.properties") and #PropertySource("remote-connection.properties"))
It works flawlessly if I place remote-connection.properties into src/main/resources, but I want that config file to be outside the resulting jar, being able to run it with
java -jar my-application.jar --spring.config.additional-location=file:/path/to/my/props/folder/,file:/path/to/another/props/folder/
What am I missing?
Thanks in advance.
Answering my own question.
Following this guide https://mkyong.com/spring/spring-propertysources-example/ I have changed my run args to
bootRun {
args = [
"--my.props.folder=/path/to/my/props/folder",
"--my.other.props.folder=/path/to/another/props/folder",
]
}
and loading props like this
#RestController
#PropertySource("file:${my.props.folder}/remote-connection.properties")
#PropertySource("file:${my.other.props.folder}/some-more.properties")
public class MyController {
#Value("${remote.ip.address}")
private String remoteIpAddress;
}
This way, it works!!

How to make externalized properties file available to spring boot integration tests?

I am developing an application using Spring Boot. I have an externalized properties file on file system. Its location is stored in an environment variable as below--
export props=file://Path-to-file-on-filesystem/file.properties
Properties from this file are loaded on classpath and are made available to application like below--
List<String> argList = new ArrayList<>();
String properties = System.getenv().get("props");
try (InputStream is = BinaryFileReaderImpl.getInstance().getResourceAsStream(properties)) {
if (is != null) {
Properties props = new Properties();
props.load(is);
for(String prop : props.stringPropertyNames()) {
argList.add("--" + prop + "=" + props.getProperty(prop));
}
}
} catch(Exception e) {
//exception handling
}
This argList is passed to SpringBootApplication when it starts like below--
SpringApplication.run(MainApplication.class, argList);
I can access all the properties using ${prop.name}
However, I do not have access to these properties when I run JUnit Integration Tests. All my DB properties are in this externalized properties file. I do not want to keep this file anywhere in the application eg. src/main/resources
Is there any way I can load these properties in spring's test context?
I could finally read the externalized Properties file using #PropertySource on linux machine. Could not get it working on Windows instance though.
Changes done are as below-
export props=/path-to-file
Note that file:// and actual file name has been removed from environment variable.
#Configuration
#PropertySource({"file:${props}/file-test.properties", "classpath:some_other_file.properties"})
public class TestConfiguration {
}
Keep this configuration class in individual projects' src/test/java folder. Thank you Joe Chiavaroli for your comment above.

Specifying relative path in application.properties in Spring

Is there a way we can lookup file resources using relative path in application.properties file in Spring boot application as specified below
spring.datasource.url=jdbc:hsqldb:file:${project.basedir}/db/init
I'm using spring boot to build a upload sample, and meet the same problem, I only want to get the project root path. (e.g. /sring-boot-upload)
I find out that below code works:
upload.dir.location=${user.dir}\\uploadFolder
#membersound answer is just breaking up the hardcoded path in 2 parts, not dynamically resolving the property. I can tell you how to achieve what you're looking for, but you need to understand is that there is NO project.basedir when you're running the application as a jar or war. Outside the local workspace, the source code structure doesn't exist.
If you still want to do this for testing, that's feasible and what you need is to manipulate the PropertySources. Your simplest option is as follows:
Define an ApplicationContextInitializer, and set the property there. Something like the following:
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext appCtx) {
try {
// should be /<path-to-projectBasedir>/build/classes/main/
File pwd = new File(getClass().getResource("/").toURI());
String projectDir = pwd.getParentFile().getParentFile().getParent();
String conf = new File(projectDir, "db/init").getAbsolutePath();
Map<String, Object> props = new HashMap<>();
props.put("spring.datasource.url", conf);
MapPropertySource mapPropertySource = new MapPropertySource("db-props", props);
appCtx.getEnvironment().getPropertySources().addFirst(mapPropertySource);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}}
Looks like you're using Boot, so you can just declare context.initializer.classes=com.example.MyApplicationContextInitializer in your application.properties and Boot will run this class at startup.
Words of caution again:
This will not work outside the local workspace as it depends on the source code structure.
I've assumed a Gradle project structure here /build/classes/main. If necessary, adjust according to your build tool.
If MyApplicationContextInitializer is in the src/test/java, pwd will be <projectBasedir>/build/classes/test/, not <projectBasedir>/build/classes/main/.
your.basedir=${project.basedir}/db/init
spring.datasource.url=jdbc:hsqldb:file:${your.basedir}
#Value("${your.basedir}")
private String file;
new ClassPathResource(file).getURI().toString()

Log4j2 in a spring web app with servlet 3.0

I need to change the default location of log4j2 configuration file. I followed the documentation here
https://logging.apache.org/log4j/2.x/manual/webapp.html
But the only file log4j2 can see is log4j2.xml in the classpath. otherwise I get "no log4j2 configuration file found"
I tried:
-1 setting context parameters
-2 setting system property Log4jContextSelector to "org.apache.logging.log4j.core.selector.JndiContextSelector". and using the JNDI selector
as described here
https://logging.apache.org/log4j/2.x/manual/webapp.html#ContextParams
-3 lookups: web, env, sys, ctx and bundle. the first 4 failed only bundle worked but you can only lookup inside the classpath.
-4 set isLog4jAutoInitializationDisabled to true, and I am not sure how to configure the filter in this case. If I include them in the web.xml the app will not deploy.
jar in the project
./WEB-INF/lib/log4j-jcl-2.4.1.jar
./WEB-INF/lib/log4j-core-2.4.1.jar
./WEB-INF/lib/log4j-slf4j-impl-2.4.1.jar
./WEB-INF/lib/log4j-api-2.4.1.jar
In my situation with .propeties file I use code shown below
#Plugin(name = "LogsConfigurationFactory", category = ConfigurationFactory.CATEGORY)
public class CustomLogsConfigurationFactory extends PropertiesConfigurationFactory {
#Override
public Configuration getConfiguration(String name, URI configLocation) {
File propFile = new File("/path_to/log4j2.properties");
return super.getConfiguration(name, propFile.toURI());
}
#Override
protected String[] getSupportedTypes() {
return new String[] {".properties", "*"};
}
}
I think you can change CustomLogsConfigurationFactory on XmlConfigurationFactory, and change return typse in getSupportedTypes method. I hope this will help you.

Jasypt with Spring ... Logger Config

While using Jasypt in Spring the logger level in the load properties method of the PropertiesLoaderSupport class is set to Info
protected void loadProperties(Properties props) throws IOException {
if (this.locations != null) {
for (Resource location : this.locations) {
if (logger.isInfoEnabled()) {
logger.info("Loading properties file from " + location);
}
The above code block returns true while using Jasypt whereas it returns false with Spring due to while I get lot of unwanted log message.
Can somebody suggest how can I make it to return false through configurations .
I am using log4j for my application as well .
Thanks
I was able to find out the result after a lot of search ... To resolve this issue we have to set the Logger properties for Jasypt in the Logger configuration being used for the application . For example
name="log4j.logger.org.jasypt">ERROR
That will resolve the issue .

Resources