java - how to provide path of file from jar to other function - java

I am creating an application (hive udf) and for that I am creating an fat jar with all the dependencies. As this is going to be distributed over multiple nodes I need to pack my property files in jar as well.
Problem
One of the function I am calling from another jar needs a parameter, a property file path. I have packed this property file inside jar. and passing just the name. However, it's complaining that file not found.
Code:
c = new ABC("classification.props");
Function inside ABC class that read the property file
public static Properties getProperties(String propFile) {
Properties props = new Properties();
try {
props.load(new FileInputStream(propFile));
return props;
} catch (IOException var3) {
var3.printStackTrace();
}
}
This ABC is coming from another jar. however I am creating a fat jar so shouldn't matter. classification.props is in the root folder of application.

The getProperties method is looking for a file on the filesystem. That's not where your file is -- your file is in the JAR.
If you can change the getProperties` method, you can tell it to read the file as a class path resource instead.
How do I load a file from resource folder?
If you can't modify the getProperties method, you can copy the properties to the filesystem (load it as a class path resource using the technique in the linked answer, and then write to to a temp directory).

Related

Java read and write to the resource folder

When someone opens my jar up I am opening a file selector gui so they can choose where they want to store their jar's files like config files and such. This should only take place the first time they open the jar. However, one issue with this approach is that I would have no way to know if it's their first time opening the jar since I will need to save the selected path somewhere. The best solution to this sounds like saving the selected path inside a file in the resource folder which is what I am having issues with. Reading and writing to this resource file will only need to be done when the program is actually running. These read and write operations need to work for packaged jar files (I use maven) and in the IDE.
I am able to read a resources file inside of the IDE and then save that file to the designated location specified in the file selector by doing this. However, I have not been able to do the same from a jar despite trying multiple other approaches from other threads.
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream is = classloader.getResourceAsStream("config.yml");
try {
if(is != null) {
Files.copy(is, testFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
So just to clarify when my project is loaded I need to listen for the user to select a valid path for files like my config. Then I want to write my config to that path which I can do from my IDE and is shown above but I cant figure this out when I compile my project into a jar file since I always receive a file not found error in my cmd. However, the main point of this post is so that I can figure out how to save that selected path to my resource folder to a file (it can be json, yml or whatever u like). In the code above I was able to read a file but I have no idea how to go from that to get the files path since then reading and writing to it would be trivial. Also keep in mind I need to be able to read and write to a resource folder from both my IDE and from a compiled jar.
The following code shows my attempt at reading a resource from a compiled jar. When I added a print statement above name.startWith(path) I generated a massive list of classes that reference config.yml but I am not sure which one I need. I assume it has to be one of the paths relating to my project or possible the META-INF or META-INF/MANIFEST.MF path. Either way how am I able to copy the file or copy the contents of the file?
final String path = "resources/config.yml";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
if(jarFile.isFile()) { // Run with JAR file
try {
final JarFile jar = new JarFile(jarFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
final String name = entries.nextElement().getName();
if (name.startsWith(path)) { //filter according to the path
System.out.println(name);
}
}
jar.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
Also if you were wondering I got the above code from the following post and my first block of code I pasted above is actually in the else statement since the IDE code from that post also did not work.
How can I access a folder inside of a resource folder from inside my jar File?
You can't write to files inside your JAR file, because they aren't actually files, they are ZIP entries.
The easiest way to store configuration for a Java application is to use Preferences:
Preferences prefs = Preferences.userNodeForPackage(MyApp.class);
Now all you have to do is use any of the get methods to read, and put methods to write.
There is absolutely no need to write files into your resource folder inside a jar. All you need to have is a smart classloader structure. Either there is a classloader that allows changing jars (how difficult is that to implement?), or you just setup a classpath that contains an empty directory before listing all the involved jars.
As soon as you want to change a resource, just store a file in that directory. When loading the resource next time, it will be searched on the classpath and the first match is returned - in that case your changed file.
Now it could be that your application needs to be started with that modified classpath, and on top of that it needs to be aware which directory was placed first. You could still setup that classloader structure within a launcher application, which then transfers control to the real application loaded via the newly defined classloader.
You could also check on application startup whether a directory such as ${user.home}/${application_name}/data exists.
If not, create it by extracting a predefined zip into this location.
Then just run your application which would load/write all the data in this directory.
No need to read/write to the classpath. No need to include 3rd party APIs. And modifying this initial data set would just mean to distribute a new zip to be extracted from.

How to refer to a file inside spring boot application jar as a -D property value while starting the app from command line?

I have a simple spring boot application that needs java.security.krb5.conf property value set to a custom krb5.conf file. I have added the file inside the src/main/resources folder and maven make it packaged into the jar.
to start the app , I run
java -jar -Djava.security.krb5.conf=<localPath>/krb5.conf my-jar.jar
currently I have to give the <localPath> as the path to the file on my machine. Is there a way to refer to the file inside the jar so that I can run in any machine without creating the file file first on the machine?
things I tried so far:
give -Djava.security.krb5.conf=classpath:krb5.conf (also ./krb5.conf). Didn't work
I see most of the examples for 'how to read files from class path' refer to getClass.getResource (filename). So I tried do getClass.getResource (filename).getPath() so that I can set it as java.security.krb5.conf system property while starting the app in main class instead of passing from command line. But the string that getPath shows is something like /<diskPath>/my-jar.jar!/BOOT-INF/classes!/krb5.conf and this path is not working when I try to read the file for testing.
Create a copy of the file to the running dir on-the-fly, in main method before calling SprinApplication.run. This is what I am doing now.
try(
InputStream in = new ClassPathResource("krb5.conf").getInputStream();
OutputStream out = new FileOutputStream( new File("krb5copy.conf"));) {
IOUtils.copy(in, out);
}
System.setProperty("java.security.krb5.conf","krb5copy.conf");
If there is a solution for this question , I can see other use cases such as providing the trustore file included in jar as javax.net.ssl.trustStore etc.
give -Djava.security.krb5.conf=classpth:krb5.conf (also ./krb5.conf).
Didn't work
It's not 'classpth' it's 'classpath', actually. AND with a slash after it.
-Djava.security.krb5.conf=classpath/:krb5.conf should work.
The code that reads the krb5.conf configuration file uses FileInputStream, which requires the path to the file and doesn't "understand" classpath: prefix.
The idea below is to locate the file on the classpath and from that to get the path to the file:
ClassPathResource resource = new ClassPathResource("krb5.conf");
try
{
String fileSpec = resource.getURL().getFile();
System.setProperty("java.security.krb5.conf", fileSpec);
}
catch (IOException e)
{
// TODO handle the exception
e.printStackTrace();
}
EDIT: When packaged as SpringBoot fat JAR, trying to read the file with FileInputStream results in java.io.FileNotFoundException: file:/<path-to-jar/<jar-name>.jar!/BOOT-INF/classes!/krb5.conf (No such file or directory) :-(

Override properties file from jar

I have a properties file in one of the .jar of my maven dependencies. I would like to override the values in my application so I created a file with the same name and the same package, but the values from the jar file are still being used. If I delete the properties file from the jar, the values of the file in my application are used. How can I always use the properties from my application instead of the .jar ?
As long as code takes your property file from class path it depends how your class path configured.
If you externalized your file out of any jar files - Try to put path to directory where your actual file located upfront of any other jar files in your java command -cp parameter.
If you keep your file inside your own jar file, in classpath - your jar file must be before that dependency jar file with default properties file.
Still those are not good solutions (sometime it is hard to control which path JVM will use first).
So, try to find documentation about your dependency jar - it may have a property to point from where and which properties file to use.
You can use Maven Resource Plugin and parametrize your configuration file so you can pass the parameters as arguments through command line
Use the properties resource in the jar as template, initial file for the properties file you will use:
Path propertiesFile = Paths.get(System.getProperty("user.home"),
".myapp/config.properties");
Files.createDirectories(propertiesFile.getParent());
if (!Files.exists(propertiesFile)) {
Files.copy(getResourceAsStrem("/config.properties"), propertiesFile);
}
Properties props = new Properties();
props.load(new FileInputStream(propertiesFile.toString());
How about this ?
rename the properties file with overridden values,
like this _override.properties (if the actual file is
called original.properties.
Now, in your code, read a system property, called 'toOverrideProps', if true, to load the overridden properties file
when running your program, you can set this property using the -Dprop=value method
This way, you have a choice on startup, to use the actual properties file or the overridden one, without conflict.

Load a properties file in a jar file

I was trying to work on a java program with requires a properties file attached to the jar. Thus, i did jar the program, and unjar it to attach a properties file. I have a class, MyClass, which requires the properties file. Thus, i basically put the properties file to the same folder as MyClass.class. Then I jarred it back.
In my MyClass.java, i was trying to access the properties by:
Properties prop = new Properties();
prop.load(MyClass.class.getClassLoader().getResourceAsStream("prop.properties"));
Where prop.properties is in the same folder as my MyClass.class.
It failed to load it. Any help will be appreciated !
Thanks,
ClassLoader.getResourceAsStream() treats the path as relative to the root of the classpath, i.e. it looks for resources at the root level of each JAR or directory on the class loader.
You probably want Class.getResourceAsStream, which treats the path as being relative to the package of the class in question. You also need to make sure to close the stream once you've read it.
Properties prop = new Properties();
InputStream in = MyClass.class.getResourceAsStream("prop.properties");
try {
prop.load(in);
} finally {
in.close();
}
If MyClass is in the package com.example then this will load com/example/prop.properties from the JAR.

Confused about java properties file location

I have simple java project with structure:
package com.abc:
a.java
b.java
c.properties
I have database configuration parameters configured in c.properties file.
Inside a.java and b.java, I am loading properties file using:
Properties p = new Properties();
InputStream in = this.getClass().getResourceAsStream("c.properties");
p.load(in);
This works fine. But the main question is, once I prepare executable jar by exporting this code, properties file also gets packaged in jar file. If someone else wants to modify properties file for different database configuration, how can he do it?
Do I have to store properties file in some fixed location in local machine. e.g. "c:/". Then give jar along with properties file to the other person. Then he needs to copy properties file inside C:/ location?
Also one more question, how can i make this location generic for windows and linux machine?
The typical way of handling this is to load the base properties from your embedded file, and allow users of the application to specify an additional file with overrides. Some pseudocode:
Properties p = new Properties();
InputStream in = this.getClass().getResourceAsStream("c.properties");
p.load(in);
String externalFileName = System.getProperty("app.properties");
InputStream fin = new FileInputStream(new File(externalFileName));
p.load(fin);
Your program would be invoked similar to this:
java -jar app.jar -Dapp.properties="/path/to/custom/app.properties"
First keep the default properties in your properties file, which gets packed into the jar. When the application starts try reading a same named properties file from some default location in filesystem, preferrable the user's home folder which you can obtain by System.getProperty("user.home");. If the file exists at the filesystem load it, if it doesn't exist then load your packed properties file and write a copy to the filesystem.
So if your properties file name is myprops.properties, initially only your jar file will contain it. When the application starts up it will check whether /home/xyz/myprops.properties file exists. Since it doesn't, it will read the packed properties file and write a copy to /home/xyz/myprops.properties file. From next time onwards, it will read from /home/xyz/myprops.properties.
Why not pass the location of the properties file as a command line argument (following a flag)? if it's not present, then use the default one in the jar file.
You're loading the properties file from the class path. I'd suggest something like this:
Properties location

Categories

Resources