Spring not looking in the Config subfolder - java

I am new to Springboot and I am having trouble with externalizing my properties files.
I have multiple ".properties" files that I have kept in a subdirectory "config/". I have removed context-placeholders from my project and have included the comma-separated properties files while executing the jar.
eg. java -jar myjar.jar --spring.config.location=file:////config/PROP1.properties, file:///config/PROP2.properties -debug
I have few questions
Why are the files in the config directory not being read even after explicitly mentioning where to look?
I have my own dependencies in the project that have same-named properties files packed in its jar. Is that creating any sort of problem when SpringBoot tries to read the files from the config folder while executing my project jar?
Update
Now I am keeping only a single properties file suppose ABC.properties outside the jar in the same directory . I am using the name "ABC" instead of "application". I am using the below command
java -Dserver.log.dir=/path/to/log/dir -jar myjar.jar --server.port=9090 --spring.config.name=ABC --prop1=val1
I have overriden a property in my external property file but I don't see the overriden value being used when I run the application. My new questions are
Is there anything wrong with the command-line?
I have the following line in xml bean configuration
<context:property-placeholder location="ABC.properties" />. Is this causing any sort of problems to detect and use the external properties?
If the above is true and I have to remove the line how will SpringBoot understand from where the property values are to be imported?
(Not related directly to the question) Is there a order that I need to follow while giving command line arguments?

Spring boot has explicit indicate how to write this external configuration. See doc ref here: https://docs.spring.io/spring-boot/docs/2.5.2/reference/htmlsingle/#features.external-config.files
Basically, you need to specify the location like this:
--spring.config.location=classpath:/somefolder/somefile.properties
--spring.config.location=file:./somefolder/somefile.properties
From your command line, it seems that you are missing one dash, and also using a wrong format of file schema.

Q1: Why are the files in the config directory not being read even after explicitly mentioning where to look?
A1: If your config folder is beside your jar file, your command should be like
java -jar myjar.jar --spring.config.location=file:./config/PROP1.properties
meanwhile, you can also use the full path to target your config file.
Q2: If I don't mention the properties files names explicitly as an argument then they won't be picked up even when they are in the config directory
A2: No. See this doc, Spring Boot will try to find config files from the four places:
A /config subdir of the current directory.
The current directory
A classpath /config package
The classpath root
If spring.config.location is not set, files in these folders named application.properties will be treated as valid config file. You can change the accepted file name by setting property spring.config.name.
Q3 About Config File Priority
A3 As described in the doc mentioned earlier, if same name properties appear in different config files, locations higher in the list override lower items.
Another tip, it will be better to remove config files inside the jar file, so you can get a full view of configuration just in one place (the externalized config folder);

Related

Read external .properties file from the install directory (not the current directory)

I am distributing a Spring Boot application as a zipped "bootJar" using the Gradle Application plugin and the "distZip" task. The end-user will get the zip file, unzip it, and run it by just typing "myApp" (a shell script nicely created by the plugin).
I would like the end-user to create a "myapp.properties" file (a name I chose) and put it in the installation directory, or a "config" directory under the installation directory.
Suppose I set up my embedded (in the jar) application.properties file as follows:
spring.config.import = file:./myapp.properties will only read from the current directory
spring.config.import = file:/etc/myapp.properties will read from the specified directory -- but I don't know what this is at build time (the end-user determines it at installation time)
How can I set up my application so that Spring Boot can read properties from an external file whose location is specified later?
NOTE: I know I can play around with the generated scripts to pass in environment variables or Spring Boot properties, but I was hoping to do this completely within Spring Boot so I don't need to modify the nicely generated shell scripts.
spring.config.import = file:./myapp.properties will only read from the
current directory spring.config.import = file:/etc/myapp.properties
will read from the specified directory -- but I don't know what this
is at build time (the end-user determines it at installation time)
Why overcomplicate this.
Place inside the jar all the properties that you want to be statically configured as default values when you build the application.
Embedded application.properties
server.port = 8080
prop1.element = something
Then the client can create another file application.properties and place it in the same directory with the jar and define more properties which are not already defined.
prop2.element = something2
prop3.element = something3
By default Spring Boot will load properties both from the embedded file as well from the file in the current directory where the jar is placed during startup.
In the external application.properties you can also overwrite properties existing in the embedded application.properties. So if the external file in the current directory same as the jar is the following
prop2.element = something2
prop3.element = something3
prop1.element = something4 <--- this value here will overwrite the value 'something' from embedded file
According to spring doc
SpringApplication will load properties from application.properties
files in the following locations and add them to the Spring
Environment:
/config subdirectory of the current directory.
The current directory
classpath /config package
The classpath root The list is ordered by
Precedence (properties defined in locations higher in the list
override those defined in lower locations).
After having more input from the comments, it seems that you face another issue as well. You start the application from command line from another directory so that is counted as the directory where spring will look for the external configuration instead of where the jar is placed.
So for example let's say that the jar is placed inside the target folder that exists in current directory. You start the application using the following command:
java -jar target/demo-0.0.1-SNAPSHOT.jar
But then the external application.properties existing inside target folder is not loaded from spring because you executed the command from another directory. This can be solved if you start the application in the following way
java -jar -Dspring.config.additional-location=./target/ target/demo-0.0.1-SNAPSHOT.jar
This should not be difficult as you already provide the path where the jar exists in the command line.

Using two yaml files for configuration properties

We are using a spring boot application, where properties are loaded from application.yml file instead of application.properties, located at src/main/resources/ which looks like below:
config:
host: localhost:8080
server: 123
And they are being pulled in a .java file like this
#ConfigurationProperties( prefix="config")
public class ConnectionImpl implements Connection{
#Value("${config.host}")
private Stringhost;
}
I am able to retrieve properties this way.
But we are trying to move the config properties from application.yml to a different .yml file which is located at a different location. (src/main/resources/env-config).
Now I am not able to retrieve properties same way, i.e, using #Value annotation. Is there any other annotation I need to add ?
From the documentation:
SpringApplication will load properties from application.properties (or application.yml) files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The class path root
If you don’t like application.properties as the configuration file name you can switch to another by specifying a spring.config.name environment property. You can also refer to an explicit location using the spring.config.location environment property (comma-separated list of directory locations, or file paths).
The default search path classpath:,classpath:/config,file:,file:config/ is always used, irrespective of the value of spring.config.location. This search path is ordered from lowest to highest precedence (file:config/ wins). If you do specify your own locations, they take precedence over all of the default locations and use the same lowest to highest precedence ordering. In that way you can set up default values for your application in application.properties (or whatever other basename you choose with spring.config.name) and override it at runtime with a different file, keeping the defaults.
You need to supply a command line argument that tells SpringApplication where specifically to look. If everything in resources/ is added to the classpath root, then your command line would look like:
java -jar myproject.jar --Dspring.config.location=classpath:/env-config/service-config.yml
If you have a general application.yml under resources/, the properties in there will still be loaded but will take a lower precedence to the properties file specified on the command line.
Your question doesn't really say what you intend to do, but if you want to have a different configuration for different environments (e.g. development, test, production), there is a simple solution for that.
Place your config files in a file hierarchy like this inside your project:
src/
main/
resources/
application.yml
application-development.yml
application-test.yml
application-production.yml
When you now start your application with
java -jar mySpringApplication.jar -Dspring.profiles.active=development
the configuration from application.yml will be taken as a "base layer", overridden by the configuration in application-development.yml. By this, you can have "default" settings for all environments in application.yml and environment-specific configuration in the application-ENV.yml files. The same works for test and production.
No.
You'll be in a much better position if you avoid hard-coding file path like that within your code base. #ConfigurationProperties used to have a locations attribute but it's deprecated and already removed in 1.5.
In Spring Boot, you configure the Environment which is a single source of truth for your configuration. Rather than having settings buried in code, you should configure Spring Boot to read the files that you want. Read the documentation for spring.config.location. If you want to do this in a more transparent manner, perhaps EnvironmentPostProcessor is what you need

Maven Dependencies that read property files

I have a java project named dbstuff which reads properties from db.properties with getClass().getResourceAsStream("/db.properties"). The values are things like connection strings, etc. This dbstuff is used in various projects most of them are web.
I now have a dropwizard project (maven) which uses dbstuff.jar as a dependency, this project is compiled as a fat jar as described here, at the moment the dbstuff only read values from db.properties if the file is present in /src/main/resources, and when the package is created the db.properties becomes embedded in the jar.
What i want to achieve is to make db.properties external, so that i can have various db.properties (one for each environment) but only one dropwizard jar, is this possible?
thank you
You could pass in the path to the external db.properties file at runtime via a system property, e.g.:
java -jar myjar.jar -DdbConfig=/path/to/db.properties
Then in your code within dbstuff.jar you can load the properties file:
final String path = System.getProperty("dbConfig");
final Properties properties = Properties.load(new FileInputStream(path));
Alternatively, as you've mentioned you are using Dropwizard, within your configuration file (the yaml file or whatever you are using) have a property which specifies where the external db.properties file is, load the properties during server initialisation and then pass them on to whatever requires it in dbstuff.jar.
You could then take this a step further, forget about the db.properties file and have an entire section in your Dropwizard config that specifies all the properties, e.g.:
db:
url: jdbc://....
user: dbuser
# etc...
And then pass on what the objects in the dbstuff.jar need. See Dropwizard's configuration documentation for more information on how to do this.
I think you can create a configuration class in DropWizard and then use it to return the correct configuration for each environment - https://dropwizard.readthedocs.org/en/latest/manual/hibernate.html.
As for your question, if you have multiple resources on the same path, even if on different jars, there is no guarantee of which one Java will be using. When you add a file named db.properties to /src/main/resources, it becomes the resource "db.properties", and is read as such:
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)

Read system properties from file specified as java executable argument

By launching java with the -D option you can set System.properties.
Is there a way to specify a properties file as an option for java, which in turn read them as System.properties?
E.g.
java -Dfile ./alotof.properties
I'm building a webapp deployed in JBoss. The jboss xml configuration files accepts system properties as inline config {my.property}, which reads from the command line argument but this gets unruly as the number of properties grow.
You can read the properties file in bash (in run.sh file), parse properties and create the proper config line for JVM.
Here you can find 2 articles that can help you:
Reading Java-style Properties Files with Shell (permalink)
Reading java.properties file from bash
I don't know about giving a direct property file as an argument, but instead you can have startup class which loads at the bootstrap and overrides whatever property you want override from your property file using System.setProperty() method.
Java can't read system properties from a file, but JBoss can - use the SystemPropertiesService, configured through properties-service.xml in the deploy directory.

Loading a configuration file from the classpath

I'm working on making my Java app easy to deploy to other computers and am writing an ant script to do so, that is going fine.
I'm having trouble loading resources that are listed in the classpath named in the manifest file of the jar.
Folder structure looks like this:
/MyProgram.jar
/lib/<dependencies>
/config/configuration.xml
I can not for the life of me access the configuration.xml file using the ClassLoader. It, along with all the dependencies are listed explicitly in the Class-Path entry to the manifest file.
I've tried many variants of the following:
this.xml = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(xmlName);
this.xml = this.getClass().getResourceAsStream(xmlName);
With xmlName as a string of all the following values:
"config/configuration.xml"
"configuration.xml"
"config.configuration.xml"
Related to this, I also have a log4j.properties file in the config directory. How do I get log4j to pick it up? Other references say it just needs to be in the classpath, and it too is explicitly named in the jar's manifest file. Can someone point me in the right direction?
Update:
Here are the actual entries from Class-Path:
Class-Path: <snip dependencies> config/configuration.xml config/log4j.properties
Classpath entries should either be directories or jar files, not individual files. Try changing your classpath to point to the config directory instead of the individual config files.
this.xml = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("config.xml");
Better yet would be to just include your config directory in MyProgram.jar. This would prevent you from needing to add it specifically to the classpath.
this.xml = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("/config/configuration.xml");
As far as I know, log4j.xml should be at root of your classpath..
and also, you can read your configuration file with below code script. and config directory should be at your classpath.
this.xml = this.getClass().getResourceAsStream("/config/configuration.xml");
You can use the log4j.configuration system property when you startup your application:
java -Dlog4j.configuration=config/log4j.properties MyApp
See http://logging.apache.org/log4j/1.2/manual.html under "Default Initialization Procedure".
Regarding the other configuration files not being picked up, what does your Manifest.mf file looks like? Are you using something like
Class-Path: config/configuration.xml lib/yourLibOne.jar lib/blah.jar
in your Manifest.mf file?
Regarding log4j : If you want it to use a different config file from the default, you can use
org.apache.log4j.PropertyConfigurator.configure(...)
Variants of this static method accept URL, Properties or file name.
There is also
org.apache.log4j.xml.DOMConfigurator
for the XML files.

Categories