152

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.

informatik01
  • 16,038
  • 10
  • 74
  • 104
Neil
  • 5,919
  • 15
  • 58
  • 85
  • 5
    The reason why I need to keep the properties file in the jar directory is that it is better to keep them together when the whole directory (including jar and property ) is copied to another machine and run. – Neil Jan 08 '12 at 08:47
  • 1
    And if I force the user to pass the property file path then he needs to change it every time he runs the batch file from a different machine. – Neil Jan 08 '12 at 08:48

9 Answers9

169

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();
}
swateek
  • 6,735
  • 8
  • 34
  • 48
ecle
  • 3,952
  • 1
  • 18
  • 22
  • 9
    This solution works. Thanks for understanding the exact requirement and detailed code.I verified that the properties file is not inside the jar file but still it could access the file from the same directory where the jar file is. In this way there not absolute path hard code is is required . Both jar and property file now can be copied to any directory and run independently. – Neil Jan 09 '12 at 07:50
  • 5
    The file will not found if you execute the command from outside for ex: {{java -jar build/main.jar}}. Do you have any fix for that, @eee? – Darian Jul 09 '15 at 03:56
  • 1
    @Darian There is nothing to fix here; It only works as designed where the jar and the properties file must be on the same `./` root folder (same directory level) as to what I have described in the file organization architecture. (as per requirement set by the original poster) – ecle Jul 09 '15 at 04:57
  • @Darian, so if you want to execute `java -jar build/main.jar`, you need to put the properties file in `build` folder as well so that it is on the same directory level as the jar. – ecle Jul 09 '15 at 05:02
  • 9
    Thanks for your response @eee, the problem is I don't know where user will execute the `java -jar path/to/jar/file`. But I found the solution in another question: `String path = ClassLoader.getSystemClassLoader().getResource(".").getPath() + "/main.properties";` – Darian Jul 09 '15 at 09:05
  • To read file as stream directly: `InputStream is = ClassLoader.getSystemResourceAsStream("./main.properties");` – Venkata Raju Oct 06 '16 at 13:09
  • I have same scenario , but only difference is that my application is a spring base and I have to configure PropertyPlaceholderConfigurer bean. But I dont know how to tell this bean that file is out side of classpath and it is beside jar file. any idea? – Mubasher Mar 10 '17 at 13:02
  • Just make sure to save your file after you have changed it - if you want updated values... – Marius Kohmann Feb 03 '22 at 10:11
52

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();
    }
  1. Get Jar file path.
  2. Get Parent folder of that file.
  3. Use that path in InputStreamPath with your properties file name.
Ninad Pingale
  • 6,801
  • 5
  • 32
  • 55
  • I had to drop the getParentFile() part so instead I used: String propertiesPath=jarPath.getAbsolutePath(); but it all depends on where the file is located – MobileMon Feb 25 '14 at 13:07
  • 4
    Just Replace "jarPath.getParentFile().getAbsolutePath();" to "jarPath.getParent()". Works like a charm then. – StackAddict Jan 07 '15 at 10:22
  • 1
    Same is the Problem in my case but I have spring base project. How to tell spring that file is beside jar file? any idea – Mubasher Mar 10 '17 at 13:00
3

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

Community
  • 1
  • 1
ddaaggeett
  • 149
  • 2
  • 10
3

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

Community
  • 1
  • 1
sethu
  • 8,181
  • 7
  • 39
  • 65
1

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.

jschnasse
  • 8,526
  • 6
  • 32
  • 72
1

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);
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Sarangz
  • 57
  • 2
0

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
pitchblack408
  • 2,913
  • 4
  • 36
  • 54
0
File parentFile = new File(".");
String parentPath = file.getCanonicalPath();
File resourceFile = new File(parentPath+File.seperator+"<your config file>");
Jay dev
  • 17
  • 7
0

One approach would be to add current directory in the classpath of your JAR using following entry in the META-INF/MANIFEST.MF file:

Class-Path: .

Then following code to load "system.properties" file will work seamlessly:

ResourceBundle systemPropertiesBundle = ResourceBundle.getBundle("system");
Bipul
  • 1,564
  • 1
  • 15
  • 16