I'm trying to use Java class Properties for saving the settings for my application between executions. Afterwards I would like to export my application into runnable JAR file and keep everything needed in this one file to provide portability. So my application has to find the properties file, load it and then save new settings into it on exit.
The loading works fine:
Properties prop = new Properties().load(this.getClass().getResourceAsStream("properties.cfg"));
But for storing I need to find the same file and overwrite it. The method Properties.store() needs an OutputStream but getResourceAsStream returns an InputStream therefore I cannot find the file and access it.
It should be always located with the *.class file. I found many solutions that work before exporting into JAR but none that would work after exporting. Thanks for any suggestions.
You can't rewrite your jar (or rather, it's complicated and not a good idea).
A preferable solution would be to read the properties from:
a directory on your machine
the .jar file
Initially location 1 wouldn't have a property file and you'd fall back to your .jar file. When you write the properties you'd write them to the nominated directory, and then on the next read you'd read your properties from this location. This would survive restarts etc.
Note that libraries such as Apache Commons Configuration automatically support this tiered properties location mechanism and may save you some time/grief in writing your own solution.
take spring's IO infrastructure (ClasspathResource)
try {
ClassPathResource r = new ClassPathResource("bdd.properties");
Properties properties = new Properties();
properties.load(r.getInputStream());
} catch (IOException e) {
Throwables.propagate(e);
}
this is real simplification.
For this kind of scenario the best approach is to add the properties file to the classpath and load it using something like ResourceBundle.
private static ResourceBundle _properties = ResourceBundle.getBundle("sample.properties");
String sampleProperty = _properties.getString(propertyKey);
How to execute :
Suppose your jar file is MyJar.jar and property file is called sample.properties
then u should write a batch or sh file around it
eg: run.sh
java -cp MyJar.jar:conf "Class with main Method"
Related
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.
In C#,when I want to create a configuration file, it's so easy,just right click the mouse and add a new configuration file, this file will be added into the solution and it's so easy to maintain.
But in java, I don't know what method is standard. I see some people use the properites file.If this is the most popular method, can some one tell me where to place this file? I saw some guy put it in the src folder, others put it in an external folder.
Can you tell me which is the standard? And what is the best practice to maintain a configuration.
I don't know if this is the "standard" way but I think it's the easiest. If you place your properties file in your project's root folder
- project
- config.properties
- src
- main
- ...
- test
When you create a File instance in Java and specify a relative filename, then the name is resolved against the directory that Java was launched from
e.g. if you launch java in your command prompt as follows:
cd C:\Users\Tom\example-project
java example-project
and this is your code:
File file = new File("tom.txt");
then the file variable will be resolved to the abolsute path: C:\Users\Tom\example-project\tom.txt
When you Run a project through Eclipse, Eclipse launches java from the root directory of the project, meaning that if you put your config file in the project's root folder then
File file = new File("name-of-config-file.properties");
will resolve to the correct config file on your system.
This has an added benefit if you create a runnable JAR, as you can just place your config file in the same directory as your JAR and the code will continue to work (the config file location will be resolved relative to the JAR).
If you put your config file in /src folder then you need to have separate code for when running from Eclipse and when running as a JAR
With regards to sample code:
//Read properties from disk
File propertiesFile = new File("config.properties");
FileReader reader = new FileReader(propertiesFile);
Properties props = new Properties();
props.load(reader);
//Set and get properties
props.setProperty("NewProperty", "value");
String propValue = props.getProperty("propToGet");
//Write properties to disk
FileWriter writer = new FileWriter(propertiesFile);
props.store(writer, "Added x properties");
Configuration files are used to store,read write user settings.
I think for web apps you can use web.xml.And for other you should use Properties class to read and write settings.
As for where to place it,If you dont specify path it is stored in your root folder other than that you have to provide explicit path.
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
I am having issues with properties file when I try to make my standalone Java aplication a runnable jar.
I have 2 properties file, depending upon the machine where its running one gets initialized.
Inside eclipse it was working fine. I was using:
Properties configProps = new Properties();
....
if(machine1)
....
configProps.load(Config.class.getResourceAsStream("machine1.properties"));
else
configProps.load(Config.class.getResourceAsStream("machine2.properties"));
It was working as Config.java and properties were in the same package.
On top of that I have log4j properties located on the root of the project.
That is also not working whne i made Jar.
How to handle the current sutuation.I know putting properties file outside jar is good idea.
How do I accomplish this.
Please help.
Putting the properties file outside of the jar is only a good idea if you need to write to that property file (it's for configuration). Given your naming, I assume it is for configuration.
For reading only, your method is fine if the properties file is properly being packaged in the Jar. Is it? Have you peaked at the contents using jar tf MyJar.jar? Does it show your properties file at the correct path?
Where to store configuration files is a broader issue. Here's a good SO article that examines a few aspects of it (namely where to put it): What is the proper way to store app's conf data in Java?
It seems to me you want to choose a location (see the above article). Once you've done that, the first time the application is run you should load the default properties from your Jar file as you are trying to do, then immediately save them to the location you've chosen. Then, and afterwards, read from and write to that location instead. You will need to use a FileInputStream/FileOutputStream.
Try adding a manifest to your JAR's META-INF with Class-Path set appropriately.
What is the best way to store and load application level properties in Java.
Is there anything simulare to .net where I would just put something in the app.config
<appSettings>
<add key="emailAddress" value="me#example.com" />
</appSettings>
And then retrieve it like this:
string AdminEmail = ConfigurationManager.AppSettings["emailAddress"];
Do I have to load properties files into file streams and such? seems like unnecessary work.
I have tried this:
Properties props = new Properties();
FileInputStream fis = new FileInputStream("myProps.properties");
props.load(fis);
fis.close();
But this is give me a java.io.FileNotFoundException exception. Where does the properties file need to go to in relation to the class?
The Preferences API provides this functionality. It has many warts, but if you are looking to do this in an OS-agnostic fashion, this is the only way to accomplish this using the standard Java runtime libraries. You can of course always write your own OS-specific code to meet your needs. Yes, you can write simple code to load a properties file, but the location of that file can become a problem across multiple operating systems. I assume since you are writing a desktop app in Java, you care about OS portability. Otherwise Java might not be the best choice for a desktop app.
If you use a FileInputStream like that, your path is relative to the current directory of the OS, which usually is the startup directory.
If you instead use the Java built in resources mechanism (as described in the API, getResourceAsStream() et al), the path will be relative to the location of your class. With this approach you can also load resources from within jars and even over networks (for Applets for instance). The concept which is used is a sort of virtual filesystem, which is called the 'classpath' in Java jargon. There is a devx article covering it a litte more in detail.
In short, this sort of code works well:
Properties prop = new Properties();
//with properties in the same dir as current class
prop.load(getClass().getResourceAsStream("my.properties"));
//with properties in the root dir of your jar, or in base of classpath
prop.load(getClass().getResourceAsStream("/my.properties"));
You will need to add error handling...
Typically it will attempt to load from the application's current directory, which can vary depending on how you run it. You should be able to determine this at runtime by doing:
String currentFolder = System.getProperty("user.dir");
The file path it would be looking for in that case is relative to where you started your java application from. This is not where the main class is or the jar file but where you called Java from. If you are starting your application with a script that calls Java, then it is that directory.
Say for example, you application is bundled in a jar file 'app.jar'. Put 'myProps.properties' in the same directory and run 'java -jar app.jar' from that directory. It should find your properties file that way.
You can use Properties with a ResourceBundle. I use this in a application to store labels, buttons and messages in different languages
First you create a properties file, like test.properties. It´s a text file and inside it you put your information like this:
propertyname=value
In your case
emailAddress=me#example.com
email2=blablabla#example.com
and so on...
To get this properties in the code, create a ResourceBundle object with the name of your property file to call the properties.
ResourceBundle rb = ResourceBundle.getBundle("test");
To get an specific value from a properties file, just call the ResourceBundle
String value = rb.getString("emailAddress");
This way, the String named value contains the value of the property named "emailAddress", located in the test.properties file
String value2 = rb.getString("email2");
Likewise, the String named value2 contains the value of the property named "email2", located in the test.properties file
When you do not specify an absolute path, the one chosen is the current one.
It's not exactly what you asked, but if you want to use XML files as configuration, you could have a look at Apache Commons Configuration