Read properties file outside JAR file - java

I have a JAR file where all my code is archived for running. I have to access a properties file which need to be changed/edited before each run. I want to keep the properties file in the same directory where the JAR file is. Is there anyway to tell Java to pick up the properties file from that directory ?
Note: I do not want to keep the properties file in home directory or pass the path of the properties file in command line argument.

So, you want to treat your .properties file on the same folder as the main/runnable jar as a file rather than as a resource of the main/runnable jar. In that case, my own solution is as follows:
First thing first: your program file architecture shall be like this (assuming your main program is main.jar and its main properties file is main.properties):
./ - the root of your program
|__ main.jar
|__ main.properties
With this architecture, you can modify any property in the main.properties file using any text editor before or while your main.jar is running (depending on the current state of the program) since it is just a text-based file. For example, your main.properties file may contain:
app.version=1.0.0.0
app.name=Hello
So, when you run your main program from its root/base folder, normally you will run it like this:
java -jar ./main.jar
or, straight away:
java -jar main.jar
In your main.jar, you need to create a few utility methods for every property found in your main.properties file; let say the app.version property will have getAppVersion() method as follows:
/**
* Gets the app.version property value from
* the ./main.properties file of the base folder
*
* #return app.version string
* #throws IOException
*/
import java.util.Properties;
public static String getAppVersion() throws IOException{
String versionString = null;
//to load application's properties, we use this class
Properties mainProperties = new Properties();
FileInputStream file;
//the base folder is ./, the root of the main.properties file
String path = "./main.properties";
//load the file handle for main.properties
file = new FileInputStream(path);
//load all the properties from this file
mainProperties.load(file);
//we have loaded the properties, so close the file handle
file.close();
//retrieve the property we are intrested, the app.version
versionString = mainProperties.getProperty("app.version");
return versionString;
}
In any part of the main program that needs the app.version value, we call its method as follows:
String version = null;
try{
version = getAppVersion();
}
catch (IOException ioe){
ioe.printStackTrace();
}

I did it by other way.
Properties prop = new Properties();
try {
File jarPath=new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String propertiesPath=jarPath.getParentFile().getAbsolutePath();
System.out.println(" propertiesPath-"+propertiesPath);
prop.load(new FileInputStream(propertiesPath+"/importer.properties"));
} catch (IOException e1) {
e1.printStackTrace();
}
Get Jar file path.
Get Parent folder of that file.
Use that path in InputStreamPath with your properties file name.

There's always a problem accessing files on your file directory from a jar file. Providing the classpath in a jar file is very limited. Instead try using a bat file or a sh file to start your program. In that way you can specify your classpath anyway you like, referencing any folder anywhere on the system.
Also check my answer on this question:
making .exe file for java project containing sqlite

I have a similar case: wanting my *.jar file to access a file in a directory next to said *.jar file. Refer to THIS ANSWER as well.
My file structure is:
./ - the root of your program
|__ *.jar
|__ dir-next-to-jar/some.txt
I'm able to load a file (say, some.txt) to an InputStream inside the *.jar file with the following:
InputStream stream = null;
try{
stream = ThisClassName.class.getClass().getResourceAsStream("/dir-next-to-jar/some.txt");
}
catch(Exception e) {
System.out.print("error file to stream: ");
System.out.println(e.getMessage());
}
Then do whatever you will with the stream

This works for me. Load your properties file from current directory.
Attention: The method Properties#load uses ISO-8859-1 encoding.
Properties properties = new Properties();
properties.load(new FileReader(new File(".").getCanonicalPath() + File.separator + "java.properties"));
properties.forEach((k, v) -> {
System.out.println(k + " : " + v);
});
Make sure, that java.properties is at the current directory . You can just write a little startup script that switches into to the right directory in before, like
#! /bin/bash
scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $scriptdir
java -jar MyExecutable.jar
cd -
In your project just put the java.properties file in your project root, in order to make this code work from your IDE as well.

I have an example of doing both by classpath or from external config with log4j2.properties
package org.mmartin.app1;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.LogManager;
public class App1 {
private static Logger logger=null;
private static final String LOG_PROPERTIES_FILE = "config/log4j2.properties";
private static final String CONFIG_PROPERTIES_FILE = "config/config.properties";
private Properties properties= new Properties();
public App1() {
System.out.println("--Logger intialized with classpath properties file--");
intializeLogger1();
testLogging();
System.out.println("--Logger intialized with external file--");
intializeLogger2();
testLogging();
}
public void readProperties() {
InputStream input = null;
try {
input = new FileInputStream(CONFIG_PROPERTIES_FILE);
this.properties.load(input);
} catch (IOException e) {
logger.error("Unable to read the config.properties file.",e);
System.exit(1);
}
}
public void printProperties() {
this.properties.list(System.out);
}
public void testLogging() {
logger.debug("This is a debug message");
logger.info("This is an info message");
logger.warn("This is a warn message");
logger.error("This is an error message");
logger.fatal("This is a fatal message");
logger.info("Logger's name: "+logger.getName());
}
private void intializeLogger1() {
logger = LogManager.getLogger(App1.class);
}
private void intializeLogger2() {
LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
File file = new File(LOG_PROPERTIES_FILE);
// this will force a reconfiguration
context.setConfigLocation(file.toURI());
logger = context.getLogger(App1.class.getName());
}
public static void main(String[] args) {
App1 app1 = new App1();
app1.readProperties();
app1.printProperties();
}
}
--Logger intialized with classpath properties file--
[DEBUG] 2018-08-27 10:35:14.510 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.513 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.513 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.513 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.513 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.514 [main] App1 - Logger's name: org.mmartin.app1.App1
--Logger intialized with external file--
[DEBUG] 2018-08-27 10:35:14.524 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.525 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.525 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.525 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - Logger's name: org.mmartin.app1.App1
-- listing properties --
dbpassword=password
database=localhost
dbuser=user

Here if you mention .getPath() then that will return the path of Jar and I guess
you will need its parent to refer to all other config files placed with the jar.
This code works on Windows. Add the code within the main class.
File jarDir = new File(MyAppName.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String jarDirpath = jarDir.getParent();
System.out.println(jarDirpath);

File parentFile = new File(".");
String parentPath = file.getCanonicalPath();
File resourceFile = new File(parentPath+File.seperator+"<your config file>");

Related

Reading from config.file throws FileNotFoundException (no such file or directory)

this is my project structure
- src
-- java
--- utils
---- ConfigFileReader
--- resources
---- config.properties
this is ConfigFileReader class:
public class ConfigFileReader {
public static String getProperty(String key) {
Properties properties = new Properties();
try {
InputStream inputStream = new FileInputStream("/config.properties");
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
return properties.getProperty(key);
}
}
and this is my config.properties file:
account_storage_technology=cognito
Unfortunately executing that method throws
java.io.FileNotFoundException: /config.properties (No such file or directory)
How can I fix?
You need to keep the resources folder in the same level as src. Like this:
- src
- java
- utils
- ConfigFileReader
- resources
- config.properties
And modify the path as below :
InputStream inputStream = new FileInputStream("resources/config.properties");
UPDATE : It is not advisable or a good idea to keep properties files inside packages and read it from there. However you can make your code read the file by showing the absolute full path.
Example :
InputStream inputStream = new FileInputStream("C:\\Project-Path\\Project-Name\\src\\java\\resources\\config.properties");
// Project-Path : This is the path where your Project Parent folder resides.
// Project-Name : This is the name of your project.
when you do '/' in the begining it will try to read from your root directory. Remove the / and as long as the config.properties is present in src/main/resources, the FileInputStream should be able to pick the config file.
ConfigFileReader.class.getResourceAsStream("config.properties")

Deploy java spring-boot app *.jar to heroku with other files in root folder

I have a spring boot app as qsysprereg2-1.0.jar. I pushed into heroku git already compiled jar file + Procfile + folder "config" with files for my app as "config/config.properties". Just some properties.
In Gradle I have only:
apply plugin: 'java'
task stage() {
println("Go stage...")
}
All compiled and deployed successfully.
In result I have error:
java.io.FileNotFoundException: config/config.properties (No such file or directory)
Of course, because:
Running bash on ⬢ qprereg... up, run.9546 (Free)
~ $ ls
Procfile qsysprereg2-1.0.jar system.properties
Where is no folder "config" from git. But "config/config.properties" had been pushed into git.
How to add the folder with files to deploy artifacts?
Sorry, but I did not find a nice solution. I made some tricks. I put all my config files in jar as resources. During starting the app I am checking the files outside jar on dick then coping from resources to dist. New files are keeping on disk without problems. Code for that:
public static void main(String[] args) {
try {
prepareConfig();
} catch (IOException ex) {
log.error("Config prepare fail.", ex);
log.throwing(ex);
throw new RuntimeException(ex);
}
SpringApplication.run(Application.class, args);
}
private static void prepareConfig() throws IOException {
File dir = new File("config");
if (!dir.exists() || !dir.isDirectory()) {
log.info("Create config directory");
Files.createDirectory(dir.toPath());
}
makeReady("config/config1.properties");
makeReady("config/config2.properties");
makeReady("config/config3.properties");
makeReady("config/configN.properties");
}
private static void makeReady(String fileName) throws IOException {
File file = new File(fileName);
if (!file.exists()) {
log.info("Create config file '{}'", file.getName());
try (final InputStream stream = Application.class.getResourceAsStream("/" + fileName)) {
Files.copy(stream, file.toPath());
}
}
}

Where to set source of application.yml in Java Spring when using classpath; currently uses sub module

I have a project with some main source code (Reading Configurations and StartUp-Listener to start several services) and multiple submodules. There are resource-folders in the main project as well as in the sub modules. Currently, I have to store the application.yml in one of the submodules; the application.yml in my main source resources is ignored.
Project-Structure:
-Main
-SubModule1
-Submodule2
-main
-resources
-config
-application.yml_
-Submodule3
-MainSourceCode (including the submodules)
-main
-resources
-config
-application.yml
Remarks:
The name of the whole project used to be the name of Submodule2 (so, there might be some config leftover)
If I rename the application.yml_ in Submodule2 to application.yml, everything works
That's, how I read the configuration:
#Configuration
#Order(100)
#PropertySources({
#PropertySource(value = "file:../../../../../resources/config/application.yml", ignoreResourceNotFound = true),
#PropertySource("classpath:/config/application.yml")
})
public class AuthorizationControllerConfiguration {
...
I receive the output:
2019-08-28 08:49:58.054 INFO 19812 --- [ main] o.s.c.a.ConfigurationClassParser : Properties location [file:../../../../../resources/config/application.yml] not resolvable: ..\..\..\..\..\resources\config\application.yml (System cannot find file)
2019-08-28 08:49:58.054 WARN 19812 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [de.......AuthorizationControllerConfiguration]; nested exception is java.io.FileNotFoundException: class path resource [config/application.yml] cannot be opened because it does not exist
Config1 is ignored as expected, Config2 is - whyever - read from the SubModule2, not from the main module
So, basically my program (run from IntelliJ btw.) selects from the wrong source.
Where do I have to look at to change the source of "classpath"?
Unfortunately, the #PropertySources annotation does not work for YML files out of the box see this blog
To resolve the issue, you will need to add a custom PropertySourceFactory.
Also, from the previously mentioned blog post:
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(#Nullable String name, EncodedResource resource) throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
// for ignoreResourceNotFound
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException)
throw (FileNotFoundException) e.getCause();
throw e;
}
}
}
This should address the issue.

How to get the root path of my project?

I have JSP projects that run in Tomcat developed in Eclipse.
I want to have some files which I store inside the project.
Here is the project structure that I have:
.settings
build
data
ImportedClasses
src
WebContent
.classpath
.project
I want to access the data folder from my code in JSP file which located in WebContent.
Tried some code below:
File userDataDirFile = new File ( "data" );
String path = userDataDirFile.getAbsolutePath();
prints
C:\Program Files\eclipse\data\users
Then
this.getClass().getClassLoader().getResource("").getPath()
prints
C:/Workspaces/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/survey/WEB-INF/classes/
Another one:
System.getProperty("user.dir")
prints
C:\Program Files\eclipse
There is no code that I tried (I think the 2nd solution supposed to work, but it doesn't) can locate me to root folder of my project. Anyone can advise?
String caminhoProjeto = Thread.currentThread().getContextClassLoader().getResource("").getPath();
This is like "src" path, but is the "classes" path of binaries files context after deployment and runtime.
My class for example of the necessarie use of this way "root dir of project":
PropertiesReader.java:
public class PropertiesReader {
public static String projectPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
public static String propertiesPath = "/META-INF/";
public static Properties loadProperties(String propertiesFileName) throws IOException {
Properties p = new Properties();
p.load(new FileInputStream(projectPath + propertiesPath + propertiesFileName));
return p;
}
public static String getText(String propertiesFileName, String propertie) {
try {
return loadProperties(propertiesFileName).getProperty(propertie);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return propertie;
}
}
PersonRegistration.java:
#ManagedBean
public class PersonRegistration {
private Integer majority = Integer.parseInt(PropertiesLeitor.getText("Brasil.properties", "pessoa.maioridade"));
}
Brasil.properties (within src / META-INF):
pessoa.maioridade = 18
Edit : File that are not in WebContent will not be deployed with your war. you have to put files used in you code inside the WebContent and try with ServletContext which point to the root folder of the your web application :
if your file is at the same folder as WEB-INF then :
ServletContext context = getContext();
String fullPath = context.getRealPath("/data");
by the way if you don't want to give direct access to data file it's recommanded to put it in WEB-INF so that no one can have access to them directly.

Reading From Config File Outside Jar Java

Currently I am trying to read my config file from root of project directory, in order to make this actual configuration I want to move this to external location and then read from there.
Adding a complete path in following code throws out error :
package CopyEJ;
import java.util.Properties;
public class Config
{
Properties configFile;
public Config()
{
configFile = new java.util.Properties();
try {
// configFile.load(this.getClass().getClassLoader().getResourceAsStream("CopyEJ/config.properties"));
Error Statement ** configFile.load(this.getClass().getClassLoader().getResourceAsStream("C://EJ_Service//config.properties"));
}catch(Exception eta){
eta.printStackTrace();
}
}
public String getProperty(String key)
{
String value = this.configFile.getProperty(key);
return value;
}
}
Here's the error:
java.lang.NullPointerException
at java.util.Properties$LineReader.readLine(Properties.java:365)
at java.util.Properties.load(Properties.java:293)
at CopyEJ.Config.<init>(Config.java:13)
at CopyEJ.CopyEJ.main(CopyEJ.java:22)
Exception in thread "main" java.lang.NullPointerException
at java.io.File.<init>(File.java:194)
at CopyEJ.CopyEJ.main(CopyEJ.java:48)
How can I fix this ?
The purpose of method getResourceAsStream is to open stream on some file, which exists inside your jar. If you know exact location of particular file, just open new FileInputStream.
I.e. your code should look like:
try (FileInputStream fis = new FileInputStream("C://EJ_Service//config.properties")) {
configFile.load(fis);
} catch(Exception eta){
eta.printStackTrace();
}
This line requires your config.properties to be in the java CLASSPATH
this.getClass().getClassLoader().getResourceAsStream("C://EJ_Service//config.properties")
When it is not, config.properties won't be accessible.
You can try some other alternative and use the configFile.load() function to read from.
One example would be:
InputStream inputStream = new FileInputStream(new File("C:/EJ_Service/config.properties"));
configFile.load(inputStream);

Categories

Resources