I have a jar that is reading a file using below code:
Thread.currentThread().getContextClassLoader().getResource(fileName);
I want to run this jar using java -jar .jar command but I want to keep this file outside my jar, so that I can edit the jar file later on without touching the jar. Can anyone help me, how to run this jar so that it will pick up the file from outside.
There are multiple approaches you can do that and it will depend on where would you like to place this external file. For the sake of this answer, I will refer to this file as config file
Not In The Same Directory
The first approach is where you will need to place this file outside the JAR and not necessarily next to the JAR file in the same directory. In that case, you can pass the file location of the config file using either an environment variable (if you are running the JAR in a shell for example) or a Java property.
To use an environment variable, assuming you are using some Linux distro, then you can use the export command to set the value; something like this:
$ export CONFIG_FILE_LOC=/etc/myapp/config.file
You can then read the value in your code using the System class by using the following code:
String fileLocationEnv = System.getenv("CONFIG_FILE_LOC");
Alternatively, you can set this as a property by adding the following segment to your launch command:
$ java -Dconfig.file.location=/etc/myapp/config.file -jar myapp.jar
You can then read the value in your code using the System class for properties using the following code:
String fileLocationProp = System.getProperty("config.file.location");
In The Same Directory
If you need the config file to co-exist in the same directory as your JAR file, then you can use the following code to get the JAR directory and then append the filename to it. Here's the code (assuming a class named MyApp)
try{
new File(MyApp.class.getProtectionDomain().getCodeSource().getLocation().toURI());
} catch(URISyntaxException exception){
System.out.println("Exception");
}
Hope that helps.
To open the file as a resoure, add the folder containing the file(s) you want to use, to your classpath:
java -classpath .;config -jar myjar.jar
This example adds the current directory and the config directory to your classpath.
Multiple folders can be specified by using a separator. On windows use ';' , on unix use ':' .
To open the file as a File, you can just use
new File("configfile")
which will look in the working directory (directory where you launched your java)
Related
my final jar file requires an argument to be passed to it at runtime. The argument is the installation directory itself. I can't modify jar file or any java code, only the argument to be passed to it in jpackage.
If it were located in C:\path\to\jar\ I would call the jar file through java -jar jarFile.jar "C:\path\to\jar", but since I'm making the msi installer with the --win-dir-chooser , the installation directory could be anything, so I don't know what to pass in --arguments.
My current solution involves a "middle man" jar file as the --main-jar. The .exe file calls the "middle man" jar which in turn calls the final jar with the needed argument(by finding the current directory through java code). However, this is seems daftly unnecessary and I would like to find a replacement for this.
Could anyone help me out? Is there a better way to do this? Any suggestions would be helpful.
Don't rely on the current directory as this could be wrong if you use shortcuts. Normally you would work out the installation directory of jpackage using System.getProperty("jpackage.app-path").
However as you are unable to change the code of the jar you can achieve the same result by defining fixed command line argument to your jar's main class by using --arguments parameter or arguments property in a launcher which references a special jpackage variable $APPDIR.
The value of $APPDIR variable is expanded at launch-time so will be filled in by the actual installation directory path to the app folder. There are three ways to hardwire the arguments to a generated EXE:
Command line flag - note that on Linux you must escape the values or the shell will fill in $APPDIR from its own environment variable:
jpackage ... --arguments $APPDIR\relpathto\yourjar.jar
With a configfile of parameters use jpackage #configfile with file configfile containing:
--arguments $APPDIR\\relpathto\\yourjar.jar
With a launcher properties file use jpackage ... --add-launcher yourappname=yourappname.properties with file yourappname.properties containing:
arguments=$APPDIR\\relpathto\\yourjar.jar
After installation your launcher definitions config RELEASEDIR\app\yourappname.cfg should contain something like:
[ArgOptions]
arguments=$APPDIR\relpathto\yourjar.jar
For above to work the jar must be packaged somewhere into the release structure such as with jpackage --input somedir and that you use the new main class or --main-jar to replace your wrapper Main - check inside the jars MANIFEST.MF.
Note that running the EXE with any command line args will replace the hardwired argument.
I have a file and a jar in same folder.
a.jar
env.properties
a.jar also contains env.properties file with different values.
When I use java -cp path_to_folder/* ClassName then java is reading the a.jar -> env.properties file content. When I use java -cp .:path_to_folder/* ClassName then java is reading env.properties file's content.
Can we determine the load order of files and jars used by java?
There are two things going on here: shell pathname expansion (aka "globbing") and then the java commands interpretation of the arguments that it sees.
Example 1:
java -cp path_to_folder/* ClassName
The shell turns that into
java -cp path_to_folder/a.jar path_to_folder/env.properties ClassName
Then java treats the path_to_folder/env.properties as if it was the class name, and fails.
Example 2:
java -cp .:path_to_folder/* ClassName
This one is a bit more tricky. The problem is that the shell tries to expand .:path_to_folder/* by interpreting .:path_to_folder/ as the name of a directory. (It doesn't know that it represents a colon-separated path.) That expansion fails, and what java sees is this:
java -cp .:path_to_folder/* ClassName
But java interprets a wildcard in the classpath as matching only JAR files. See Setting the Classpath
So the above is equivalent to this:
java -cp .:path_to_folder/a.jar ClassName
and the properties file is not on the effective classpath.
Solution:
If you want both the JAR and properties file on the classpath, you need to do this:
java -cp .:path_to_folder/a.jar:path_to_folder ClassName
Now both the JAR file and the folder containing the properties file are on the effective classpath, and the application will be able to read the properties file as a resource using the resource path /env.properties. (It should also read the JAR file as a resource as /a.jar.)
This is not a direct answer to your question but may solve your task:
You could try to read the properties in two steps:
Read your jar-internal env.properties as you do already from classpath.
Read your jar-external env.properties (which you should place then outside your classpath) via filesystem access:
Properties properties = new Properties();
properties.load(new FileInputStream(new File("./env.properties")));
And then decide (dependent of which is available) which properties to use.
java -cp path_to_folder/* ClassName
Yes, because this means the 'raw' env file (the one not in the jar) is not even on the classpath. The above line of code doesn't work at all except on windows (it should be put on quotes on all other platforms): That star needs to arrive unmolested by the shell as an argument straight to the java executable, and this means: all jars in this directory. Not the directory itself.
Hence, yes, of course, this means only the env file in the jar is on the classpath. By definition.
java -cp .:path_to_folder/* ClassName
Yes, now both are on the classpath. I think it then goes in order; ./env.properties works, so that wins, as . is the first entry in the classpath. Yeah, path_to_folder/foo.jar!/env.properties would also work, but classpath scanning stops on a hit, unless you are using a ClassLoader's findResources option (which would find both of them).
I would like to be able to run a jar file java -jar myapp.jar on different folders and have it load config.properties based on the executing context.
/myapp/myapp.jar
/folder1/config.properties
/folder2/java -jar /myapp/myapp.jar <------ loads /folder1/config.properties
/some/folder2/config.properties
/some/folder2/java -jar /myapp/myapp.jar <------ loads /folder2/config.properties
Once the properties are loaded, I want it to then create some files in the current execution folder.
So:
How do I tell java to load a properties file based on the current executing context?
How do I get access the folder that the jar was executing from?
When you provide a relative path to the constructor of the class File like new File("config.properties"), behind the scene, the absolute path built is
System.getProperty("user.dir") / config.properties
with user.dir that is actually the User working directory which is also the directory from which you launch your command.
How do I get the location of the executed jar? I found a lot of solutions but none of them work for me. When I run them in my IDE everything is fine. As soon as I build a jar file and run it with java -jar myapp.jar the output is like /.../myapp.jar!/foo/bar
I will run the code in myapp.jar - not in any library.
Location of jar: /home/me/dev/myapp/myapp.jar
Expected output: /home/me/dev/myapp/
I don't want the working directory as I would get with System.getProperty("user.dir");
Why I want to do this:
I want to store and load a file beside the actual jar. Like
/home/me/bin/myapp/myapp.jar
/home/me/bin/myapp/license.key
I want to avoid storing the file into some generic folder like System.getProperty("user.home");. And I don't want to store the file within the jar file.
java.nio.file.Paths.get(".").toAbsolutePath() will return absolute path to current directory.
I use something along these lines:
[YourClass].class.getProtectionDomain().getCodeSource().getLocation().getPath()
Regards
Is there a way to read a file from a network shared location on windows?
Let's say for instance I've this simple code that reads a text file called readMe.txt from the Addons folder.
import java.io.File;
class Sample{
public static void main(String[] ar){
File file = new File("Addons/ReadMe.txt");
System.out.println(file.getAbsolutePath());
//followed by printing the contents of file
}
}
And I execute this file using a windows batch runme.bat that has
java Sample
PAUSE
The bat runs and executes the above class only when I place the Addons folder with ReadMe.txt, Sample.class, runme.bat file in my local drive.
When it is placed in a network shared location with UNC path like \\name\Shared
In such a scenario, the bat file typically starts the base from C:\Windows and throws a classNotFoundException. I can rather map the shared drive to a *Z:* drive or whatever but I do not want to do it.
I want the code to programatically detect and retrieve the content of Readme.txt in Addons folder irrespective of whether it is being executed on a local drive or on a shared drive. Is there a way to achieve this? Please help..
Thanks
Veekay
When using a file path in Java, make sure to escape all \ correctly when giving the full path name.
For example, if the file is on PC with IP (10.10.10.123) on a Shared folder called Addons then the full path will be:
File f = new File ("\\\\10.10.10.123\\Addons\\readme.txt");
Other than the full path, your code is throwing a ClassNotFound because you JAVA-CLASSPATH is not set properly.
In your bat file %~dp0 expands to the location of the bat file. You need that in your classpath so that java can find the class, though I don't know if it will choke on UNC path. For example:
#echo off
echo %~dp0
would output
\\host\share\dir
EDIT: %dp0 will not work if there are spaces. This is what you need in your bat file:
#echo off
set p=%~dps0
echo %p%
java -classpath %p%\jarname classname
pause
Two ways of doing.
1) Map the shared path to a local drive.
2) Else hard code the server path in new File('') as mentioned by Medopal.
Something like new File("").getAbsolutePath() might help me get the base Folder when executed on a local system. Likewise there is no such way to programmatically find out the working base when executed on a shared location.