I need a configuration file (Properties) for this project I'm working on.
The issue is that the Properties instance fails to load from the file (no exceptions, no visible problems) although it can store properly.
Because I have a defaults HashMap, any property that doesn't exist has it's default value placed in the Properties instance, which then stores everything, so that new properties are seamlessly added when the production server is updated.
I've been tracking this bug for hours, and I can't fix it. I've read dozens of questions here on StackOverflow as well as code examples on other sites. Nothing helped.
The one reason I haven't dropped it already and used the DB instead is that the JDBC driver URL, user and password are stored in that file as well. Notice that the file is being read and written to the hard drive.
Since the defaults system puts stuff in place, even if the file doesn't exist when I try to read, after it's saved it appears, but the next run still won't read anything. I noticed the bug after I changed a setting, and checked the file after a few runs, and to my shock, all values were default.
What's currently happening is the following:
1) No matter if the file is there or not, Properties will not load anything.
2) Since there's nothing in the Properties instance, it is filled with defaults.
3) The instance will now save, overwriting the file with the default values.
Here's all the relevant code:
private static Properties getConfig(){
Properties properties = new Properties();
File cfgFile = new File("data/titallus.properties");
try{
if(cfgFile.createNewFile()){
System.out.println("Config file not found. A default config file will be created automatically.");
}
FileReader reader = new FileReader(cfgFile);
FileWriter writer = new FileWriter(cfgFile);
properties.load(reader);
reader.close();
System.out.println(properties); // Debug, always prints '{}'
for(String k : defaults.keySet()){
if(!properties.containsKey(k)){
properties.setProperty(k, defaults.get(k));
}
}
properties.store(writer, "Titallus Configuration File");
writer.close();
}catch(Exception e){
e.printStackTrace();
System.exit(-1);
}
return properties;
}
I have tried everything I could think of, to no avail.
I also have a Properties subclass for multi-language support, which works just fine.
Does anyone have any idea how to fix this, or at least, another approach to this?
FileWriter writer = new FileWriter(cfgFile);
will be erasing your file before you read from it.
You create a FileWriter for the file before you load the file, which clears the existing data.
Related
I have a java web applilication. This application loads its configuration file with singleton according source file below.
public class Configuration {
private static Configuration config;
private static Properties prop = null;
private static InputStream input = null;
private Configuration() {
input = this.getClass().getResourceAsStream("/config.properties");
prop = new Properties();
prop.load(input);
input = new FileInputStream(prop.getProperty("soap.config"));
prop = new Properties(prop);
prop.load(new InputStreamReader(input, StandardCharsets.UTF_8));
}
public static Configuration getInstance() {
if (config == null) {
config = new Configuration();
}
return config;
}
}
config.properties (located into resources folder of java project)
soap.config=/home/wildfly/soap.propeties
Content of soap.properties file:
server=192.168.1.1
user=John
pass=thepass
Server features:
Total memory: 8GBram. 30% ram used
1 core, 40gb hard disk
Widfly Server
linux virtual machine
If I want to change some value in config file, it's necessary to restart the aplication by Wildfly admin console also. I think it's more useful to change config file values without restarting the application.
Additionally, the application receives more than thousands request a day and I see server status is fine.
Questions:
Is it beneficial or worth to use singleton to load a configuration file?
The instruction this.getClass().getResourceAsStream("/config.properties") will read the config file and after that it will close it inmediatelly. Is it correct?
Let's break this into two pieces: is a singleton ok for a config file? Kind of. Singletons have some major flaws when it comes to testability. It's better in general to use injection, and to pass an instance of the class to every place that needs it. A singleton will work, but it will make your testing far harder.
Is there a better way to handle config files so they handle changes? Yes. Use a WatchService to watch for when the file is changed (https://docs.oracle.com/javase/tutorial/essential/io/notification.html). The Configuration class should handle this. Then when it does change, it should parse the new file and update itself. This does provide a gap for race conditions though where part of the data is fetched from the old file, and part from the new. There's techniques you can use to avoid that however (providing all the data atomically, or allowing a client to lock the configuration file and only updating when its unlocked, etc).
The best solution I can think of:
simply give the config another field: lastFileEditedTime that stores the timestamp of the file that was loaded last.
create a static variable: lastFileUpdateCheckedTime. this stores the last check time in ms (System.getCurrentMilis) and if the last check has been made more than x seconds ago, check that file again
use both in the getInstance() method. First check against lastFileUpdateCheckedTime, and if that triggers, check against lastFileEditedTime
(you could also make both static or add both to the config, however you like)
This way the system keeps loading updated files, but will not reload too many times per second or do the filesystem timestamp check too often.
And to answer your questions:
yes, singleton is beneficial, because it prevents loading each time, and all parts of your code that use it are (more or less) on the same config
no, getResourceAsStream will give you an open stream, you should close it. Activate compiler warnings, they will clearly show you. Best use try-resource-catch for closing
This is an issue I have had in many applications.
I want to change the information inside a file, which has an outdated version.
In this instance, I am updating the file that records playlists after adding a song to a playlist. (For reference, I am creating an app for android.)
The problem is if I run this code:
FileOutputStream output = new FileOutputStream(file);
output.write(data.getBytes());
output.close();
And if an IOException occurs while trying to write to the file, the data is lost (since creating an instance of FileOutputStream empties the file). Is there a better method to do this, so if an IOException occurs, the old data remains intact? Or does this error only occur when the file is read-only, so I just need to check for that?
My only "work around" is to inform the user of the error, and give said user the correct data, which the user has to manually update. While this might work for a developer, there is a lot of issues that could occur if this happens. Additionally, in this case, the user doesn't have permission to edit the file themselves, so the "work around" doesn't work at all.
Sorry if someone else has asked this. I couldn't find a result when searching.
Thanks in advance!
One way you could ensure that you do not wipe the file is by creating a new file with a different name first. If writing that file succeeds, you could delete the old file and rename the new one.
There is the possibility that renaming fails. To be completely safe from that, your files could be named according to the time at which they are created. For instance, if your file is named save.dat, you could add the time at which the file was saved (from System.currentTimeMillis()) to the end of the file's name. Then, no matter what happens later (including failure to delete the old file or rename the new one), you can recover the most recent successful save. I have included a sample implementation below which represents the time as a 16-digit zero-padded hexadecimal number appended to the file extension. A file named save.dat will be instead saved as save.dat00000171ed431353 or something similar.
// name includes the file extension (i.e. "save.dat").
static File fileToSave(File directory, String name) {
return new File(directory, name + String.format("%016x", System.currentTimeMillis()));
}
// return the entire array if you need older versions for which deletion failed. This could be useful for attempting to purge any unnecessary older versions for instance.
static File fileToLoad(File directory, String name) {
File[] files = directory.listFiles((dir, n) -> n.startsWith(name));
Arrays.sort(files, Comparator.comparingLong((File file) -> Long.parseLong(file.getName().substring(name.length()), 16)).reversed());
return files[0];
}
I need to write a custom batch File renamer. I've got the bulk of it done except I can't figure out how to check if a file is already open. I'm just using the java.io.File package and there is a canWrite() method but that doesn't seem to test if the file is in use by another program. Any ideas on how I can make this work?
Using the Apache Commons IO library...
boolean isFileUnlocked = false;
try {
org.apache.commons.io.FileUtils.touch(yourFile);
isFileUnlocked = true;
} catch (IOException e) {
isFileUnlocked = false;
}
if(isFileUnlocked){
// Do stuff you need to do with a file that is NOT locked.
} else {
// Do stuff you need to do with a file that IS locked
}
(The Q&A is about how to deal with Windows "open file" locks ... not how implement this kind of locking portably.)
This whole issue is fraught with portability issues and race conditions:
You could try to use FileLock, but it is not necessarily supported for your OS and/or filesystem.
It appears that on Windows you may be unable to use FileLock if another application has opened the file in a particular way.
Even if you did manage to use FileLock or something else, you've still got the problem that something may come in and open the file between you testing the file and doing the rename.
A simpler though non-portable solution is to just try the rename (or whatever it is you are trying to do) and diagnose the return value and / or any Java exceptions that arise due to opened files.
Notes:
If you use the Files API instead of the File API you will get more information in the event of a failure.
On systems (e.g. Linux) where you are allowed to rename a locked or open file, you won't get any failure result or exceptions. The operation will just succeed. However, on such systems you generally don't need to worry if a file is already open, since the OS doesn't lock files on open.
// TO CHECK WHETHER A FILE IS OPENED
// OR NOT (not for .txt files)
// the file we want to check
String fileName = "C:\\Text.xlsx";
File file = new File(fileName);
// try to rename the file with the same name
File sameFileName = new File(fileName);
if(file.renameTo(sameFileName)){
// if the file is renamed
System.out.println("file is closed");
}else{
// if the file didnt accept the renaming operation
System.out.println("file is opened");
}
On Windows I found the answer https://stackoverflow.com/a/13706972/3014879 using
fileIsLocked = !file.renameTo(file)
most useful, as it avoids false positives when processing write protected (or readonly) files.
org.apache.commons.io.FileUtils.touch(yourFile) doesn't check if your file is open or not. Instead, it changes the timestamp of the file to the current time.
I used IOException and it works just fine:
try
{
String filePath = "C:\sheet.xlsx";
FileWriter fw = new FileWriter(filePath );
}
catch (IOException e)
{
System.out.println("File is open");
}
I don't think you'll ever get a definitive solution for this, the operating system isn't necessarily going to tell you if the file is open or not.
You might get some mileage out of java.nio.channels.FileLock, although the javadoc is loaded with caveats.
Hi I really hope this helps.
I tried all the options before and none really work on Windows. The only think that helped me accomplish this was trying to move the file. Event to the same place under an ATOMIC_MOVE. If the file is being written by another program or Java thread, this definitely will produce an Exception.
try{
Files.move(Paths.get(currentFile.getPath()),
Paths.get(currentFile.getPath()), StandardCopyOption.ATOMIC_MOVE);
// DO YOUR STUFF HERE SINCE IT IS NOT BEING WRITTEN BY ANOTHER PROGRAM
} catch (Exception e){
// DO NOT WRITE THEN SINCE THE FILE IS BEING WRITTEN BY ANOTHER PROGRAM
}
If file is in use FileOutputStream fileOutputStream = new FileOutputStream(file); returns java.io.FileNotFoundException with 'The process cannot access the file because it is being used by another process' in the exception message.
I want to add new property to an existing file. Whenever, I add the new property, the entire file gets overwritten. Is there a way to update the file and not overwrite property.
FileOutputStream fo = new FileOutputStream(PROPERTIES_FILE);
Properties pr = new Properties();
pr.setProperty("Key1", "KeyValue");
try {
pr.store(fo, " Comments");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
(1) Now if I want to add a new property called Key2 and the set a value KeyValue2. Is it possible ?
(2) Also when I deploy in tomcat, only when I give the absolute path, the file is getting updated. Is there a way to find the file location at runtime. Because when I run test case, the file will be present locally and the path will be different.
(3) Is there a way to leverage classpath in anyway for this.
Thanks in advance!
**I am writing it down since I did not find clear answer for the problem mentioned **
(1) Now if I want to add a new property called Key2 and the set a value KeyValue2. Is it possible ?
**Yes it is possible. The key to understanding here is that, the properties object will be store on calling 'store' api. Here appending does not happens. The file will be overwritten by the contents of the properties object. THE API DOES NOT SUPPORT APPENDING IT.
The solution this problem will be to :
1) Load the properties from the same file
2) Update the new property or existing property
3) then store using the output stream
IN THIS WAY THE CONTENT OF THE FILE WILL NOT BE LOST**
(2) Also when I deploy in tomcat, only when I give the absolute path, the file is getting updated. Is there a way to find the file location at runtime. Because when I run test case, the file will be present locally and the path will be different.
** There are two ways to do it
1) Make sure that the file is present in the classpath. If present in the classpath, we need not give absolute path before the filename
2) Provide another class which set the path. In this way, the path can be set when running the testcases. (TESTNG/JUNIT)**
(3) Is there a way to leverage classpath in anyway for this.
** Already covered above **
Hope this helps
Hi i have made a small program that reads a config file. This file is stored outside the actual jar file. On the same level as the jarfile actually.
When i start my program from a commandline in the actual directory (ie. D:\test\java -jar name.jar argument0 argument1) in runs perfectly.
But when i try to run the program from another location then the actual directory i get the filenotfound exception (ie. D:\java -jar D:\test\name.jar argument0 argument1).
The basic functionality does seem to work, what am i doing wrong?
As requested a part of the code:
public LoadConfig() {
Properties properties = new Properties();
try {
// load the properties file
properties.load(new FileInputStream("ibantools.config.properties"));
} catch (IOException ex) {
ex.printStackTrace();
} // end catch
// get the actual values, if the file can't be read it will use the default values.
this.environment = properties.getProperty("application.environment","tst");
this.cbc = properties.getProperty("check.bankcode","true");
this.bankcodefile = properties.getProperty("check.bankcodefile","bankcodes.txt");
} // end loadconfig
The folder looks like this:
This works:
This doesn't:
The jar doesn't contain the text file.
When reading a File using the String/path constructors of File, FileInpustream, etc.. a relative path is derived from the working directory - the directory where you started your program.
When reading a file from a Jar, the file being external to the jar, you have at least two options :
Provide an absolute path: D:/blah/foo/bar
Make the directory where your file is located part of the class path and use this.getClass().getClassLoader().getResourceAsStream("myfile")
The latter is probably more appropriate for reading configuration files stored in a path relative to the location of your application.
There could be one more possibility:
If one part of your code is writing the file and another one is reading, then it is good to consider that the reader is reading before the writer finishes writing the file.
You can cross check this case by putting your code on debug mode. If it works fine there and gives you FileNotFoundException, then surely this could be the potential reason of this exception.
Now, how to resolve:
You can use retry mechanism something similar to below code block
if(!file..exists()){
Thread.sleep(200);
}
in your code and change the sleep value according to your needs.
Hope that helps.!!