14

The question says it all.

The specialty in my case is that the current working directory is not the location of the jar file but c:\Windows\system32 (My jar file is started by windows using the right-click-menu, i want to pass a folder's path as a parameter to the jar).

Now I want to load a configuration file called config.xml that is in the same folder as the jar. The purpose of the file is, of course, to provide settings for the jar. It is important for me that the xml file is outside of the jar file for easy editing.

I'm having a hard time loading that file. Windows executes the line

cmd /k java -jar D:\pathToJarfile\unpacker-0.0.1-SNAPSHOT-jar-with-dependencies.jar 

Calling the whole thing with cmd /k leaves the windows command prompt open so that I can see the jar's output.

I cannot use new File(".") or System.getProperty("user.dir") for the relative path as these functions return C:\Windows\system32\. and C:\Windows\system32, respectively (which is the working folder for everything that windows executes AFAIK).

I've had no success with Launcher.class.getResourceAsStream("/../config.xml") either. Since that path begins with /, searching starts at the root node of the jar. Going to ../config.xml point exactly to that file, but the call returns null.

Can someone point me in the right direction? I'm really stuck here. This file loading stuff really bugs me everytime...

Requirements by myself:

  • I don't want to hardcode the path in the java source code
  • I don't want to pass the file's path as a parameter to the java -jar call (neither as a param to the main(String[] args) nor using -Dpath=d:\... to set a system property)

In addition to the original problem, I had a hard time to have maven2 place Class-Path: . into the MANIFEST.MF (The solution that BalusC posted) when using jar-with-dependencies. The problem was that the line appeared in the regular jar's MANIFEST file, but not the jar-with-dependencies.jar's MANIFEST file (2 jar files are generated). For anyone who cares how i did it:

<plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2-beta-5</version> <configuration> <archive> <manifest> <mainClass>${mainClass}</mainClass> <addClasspath>true</addClasspath> <!--at first, i tried to place the Class-Path entry right here using <manifestEntries>. see below --> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <goals> <goal>attached</goal> </goals> <phase>package</phase> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>${mainClass}</mainClass> </manifest> <!--this is the correct placement --> <manifestEntries> <Class-Path>.</Class-Path> </manifestEntries> </archive> </configuration> </execution> </executions> </plugin> 

2 Answers 2

13

Here is one possible solution using Class.getProtectionDomain():

final Class<?> referenceClass = YourMainClass.class; final URL url = referenceClass.getProtectionDomain().getCodeSource().getLocation(); try{ final File jarPath = new File(url.toURI()).getParentFile(); System.out.println(jarPath); // this is the path you want } catch(final URISyntaxException e){ // etc. } 

YourMainClass can be replaced by any class in your jar.


From the Class.getProtectionDomain() docs:

Returns the ProtectionDomain of this class. If there is a security manager installed, this method first calls the security manager's checkPermission method with a RuntimePermission("getProtectionDomain") permission to ensure it's ok to get the ProtectionDomain. Returns: the ProtectionDomain of this class Throws: SecurityException - if a security manager exists and its checkPermission method doesn't allow getting the ProtectionDomain. 
Sign up to request clarification or add additional context in comments.

1 Comment

This is really nice to know in case I'll need the path in the code. Currently I don't, so I'll try to use BalusC's answer.
8

To get Launcher.class.getResourceAsStream("/../config.xml") to work, you need to add its path to the Class-Path entry of the JAR's MANIFEST.MF file. This is the normal practice.

5 Comments

Do i need to place the absolute path into the MANIFEST.MF or does .. suffice? The latter would be desirable. Sorry for asking instead of trying, I don't have my IDE at this machine.
No, the path relative to the JAR file itself. If you want to have the config.xml in the same folder as the JAR file itself, then Class-Path: . suffices (don't forget to add a blank line to end of MANIFEST.MF!). Then you can obtain the config.xml by getResourceAsStream("config.xml"). Keep it simple :)
This is a very nice approach, but I can't get it to work. The reason is the maven assembly plugin that generates the jar-with-dependencies. In the regular .jar file, my MANIFEST.MF is being put into the jar correctly, but the jar-with-dependencies file does not use the manifest i specified :-/ Does anyone know a quick solution to this?
Sorry, I don't use Maven, so I can't go in detail. You should at least specify somewhere an extra dependency path to be included in classpath. By the way, a correction to the previous comment, if the Launcher class itself is in a different package, you should have used "/config.xml" as resource name. Otherwise it is looking relative to the location of Launcher class (inside JAR).
I updated the question because I figured the maven2 stuff out. I can now load the config.xml using InputStream fromFile = Launcher.class.getResourceAsStream("/config.xml"); Great answer, thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.