How do I update a properties file dynamically? - java

My application is a batch process that pulls environment variables from an application.properties file. The password that I use must be updated every few months. I would like to automate the password update process and save the updated password in the properties file so it can be used in future runs, but any updates I try to make are not propagated to the application.config file.
When I run in Intellij, there are no errors, but the file is not updated. When I run the application as a jar, it gives:
java.io.FileNotFoundException: file:{localpath}\application.jar!\BOOT-INF\classes!\application.properties (The filename, directory name, or volume label syntax is incorrect)
How do I dynamically update the application.properties file within the jar or do I need to create a new properties file outside of the jar when the application first runs??
EXAMPLE CODE SNIPPETS BELOW:
properties.config:
username=user
password=password123
Application.java:
//get properties
prop = new Properties();
InputStream input = null;
try {
input = new FileInputStream(f);
// load a properties file
prop.load(input);
} catch (IOException ex) {
logger.error("Error reading application.properties", ex);
ex.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//update properties
prop.setProperty("TESTSTRING", "testvalue");
//write to properties file
OutputStream os = null;
try{
File f = new File(Application.class.getClassLoader().getResource("application.properties").getFile());
os = new FileOutputStream(f);
prop.store(os, comments);
}catch(IOException ex){
logger.error("Error updating application.properties", ex);
ex.printStackTrace();
} finally {
if(os != null){
try{
os.close();
} catch (IOException e){
e.printStackTrace();
}
}

Properties of an application that may change according to the running environment should never be packaged inside the packaged component.
It should be not hard coupled to the component and it should also be modifiable if required without modifying the packaged component.
Besides, properties may contain confidential information (as in your case : username and password).
So it should be preferably stored directly on the target environment.
For example, one of the cloud native application principles from Heroku/twelve factors (that are also good practices in general) is "storing the configuration in the environment".
So updating the properties file inside the packaged component has to be absolutely avoided for the same reasons.
Besides doing it makes the state application more hard to reproduce if an errors occurs.
In your case, simply fill username and password properties in a specific properties file according to the target environment and store this file on the environment.
Then you have just to run the application by specifying this properties file.
For example for windows :
java -jar yourApp-1.0.jar --spring.config.location=file:///D:/application-target-env.properties
Here is the reference documentation about it :
24.2 Accessing command line properties

Related

Java throws java.nio.file.NoSuchFileException, but file exists

I'm running a Spring REST application inside a docker container. I have a function inside a Spring controller for saving images and a function for reading them. The function for saving works properly but I have an issue with the function for reading them:
public byte[] getByteArray(String fileName) {
try {
File f = new File("/upload/" + fileName);
return Files.readAllBytes(f.toPath());
} catch (IOException e) {
e.printStackTrace(); // this is for testing
return null;
}
}
However after I use the above function I get this error java.nio.file.NoSuchFileException: /upload/test.png. I checked and this file exists in this directory. What could be the reason Java can't see this file?
Most likely your /upload directory is not accessible to the java process. directories have access rights, an owner, and a group. There is one set of rights for the owner, one for the group, and one for the rest.

JavaFX:Editable Configuration Files After Packaging

I've a JavaFX application that I packaged it using antBuild to build a single installer .exe file, my app have some configuration files that was placed in the root of the project this way i load them from the root of the project in order to they can be place beside the .jar file and could be changable:
try {
File base = null;
try {
base = new File(MainApp.class.getProtectionDomain().getCodeSource().getLocation().toURI())
.getParentFile();
} catch (URISyntaxException e) {
System.exit(0);
}
try {
File configFile = new File(base, "config.properties");
}
so after packaging the app even if I put the files manually in the same place with jar file, again the app can not recognize them and put into error.
So what is the proper way to store and where to store some sort of config files and how to add them to the installer to put it to right place during installation?
If your application is bundled as a jar file, then MainApp.class.getProtectionDomain().getCodeSource().getLocation().toURI() will return a jar: scheme URI. The constructor for File taking a URI assumes it gets a file: scheme URI, which is why you are getting an error here. (Basically, if your application is bundled as a jar file, the resource config.properties is not a file at all, its an entry in an archive file.) There's basically no (reliable) way to update the contents of the jar file bundling the application.
The way I usually approach this is to bundle the default configuration file into the jar file, and to define a path on the user file system that is used to store the editable config file. Usually this will be relative to the user's home directory:
Path configLocation = Paths.get(System.getProperty("user.home"), ".applicationName", "config.properties");
or something similar.
Then at startup you can do:
if (! Files.exists(configLocation)) {
// create directory if needed
if (! Files.exists(configLocation.getParent())) {
Files.createDirectory(configLocation.getParent());
}
// extract default config from jar and copy to config location:
try (
BufferedReader in = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/config.properties")));
BufferedWriter out = Files.newBufferedWriter(configLocation);) {
in.lines().forEach(line -> {
out.append(line);
out.newLine();
});
} catch (IOException exc) {
// handle exception, e.g. log and warn user config could not be created
}
}
Properties config = new Properties();
try (BufferedReader in = Files.newBufferedReader(configLocation)) {
config.load(in);
} catch (IOException exc) {
// handle exception...
}
So this checks to see if the config file already exists. If not, it extracts the default config from the jar file and copies its content to the defined location. Then it loads the config from the defined location. Thus the first time the user runs the application, it uses the default configuration. After that, the user can edit the config file and subsequently it will use the edited version. You can of course create a UI to modify the contents if you like. One bonus of this is that if the user does something to make the config unreadable, they can simply delete it and the default will be used again.
Obviously this can be bullet-proofed against exceptions a little better (e.g. handle case where the directory is unwritable for some reason, make the config file location user-definable, etc) but that's the basic structure I use in these scenarios.

How do I add properties to a properties file using Java?

I have a real quick question. I have a Java program in which I use a properties file. The file is used for keeping track of the program's users. My problem is I cannot figure out how to ADD to the file. I know how to set existing properties to a value, but I don't know how to add more properties without over writing the other ones.
I would like the program to 'register' users, so to speak. Whenever a new users 'signs up', I want the program to add a new property containing the new user's information. I run into this problem though:
Example:
File: numOfUsers=0
One user registers. The username is 'c00lGuy'. The program registers this in the file:
File: numOfUsers=1 user1-username=c00lGuy
Another user registers. She decides to call her username 'theGr8Girl'. The program registers this:
File: numOfUsers=2 user2-username=theGr8Girl
The file after the two users registered:
File: numOfUsers=2 user2-username=theGr8Girl
How do I prevent my program from overwriting existing lines in the file? It seems to erase the file's contents, and then add what I tell it to. I don't want it to erase the file's contents.
The code I am using to register the properties:
Properties prop = new Properties();
OutputStream output = null;
int userCount = getUserCount();
userCount++;
try {
output = new FileOutputStream(fileName);
// set the properties value
prop.setProperty("numOfUsers", String.valueOf(userCount));
prop.setProperty("user" + userCount + "-username", username);
// save properties to project root folder
prop.store(output, null);
} catch (IOException io) {
io.printStackTrace();
} finally {
if (output != null)
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Try something like this:
FileOutputStream out = new FileOutputStream(fileName);
props.setProperty("numOfUsers", 2);
...
props.store(out, null);
out.close();
Properties files aren't really intended for this sort of usage, but if you have a small enough data set it'll work.
The step you are missing is that you need to read the properties from disk, make the changes, then save them back to disk.
Properties props = new Properties();
try{
props.load(inputStream);
} finally {
inputStream.close();
}
props.setProperty(....);
try{
props.store(outputStream);
} finally {
outputStream.close();
}
Just bear in mind that this is not at all suitable for any sort of volume processing. Also, there is a race condition if you have two threads trying to make changes to the properties file at the same time.
If you are looking for a lightweight persistent store, I highly recommend mapdb.
You code is creating a new Properties object each time. Make sure to reuse the old instance when adding a user.
The typical format for a line in this file would be
user=hashedPassword
so use the username as the key and the password as a value. The number of users does not need to be stored, it is just the size of the properties map.

Log4j doesn't see .properties file

I use log4j for logging in my app. In every class I need to log something I have the following:
Properties props = new Properties();
try {
props.load(new FileInputStream("/log4j.properties"));
} catch (Exception e){
LOG.error(e);
}
PropertyConfigurator.configure(props);
log4j.properties is placed to the folder /src/main/resources/
the path /log4.properties is given by IDEA as copy reference. When I start my app it it shows the FileNotFoundException
Don't use FileInputStream.
The java.io and its consorts work with the current working directory i.e the directory from which the JVM is executed and not the code workspace.
to illustrate this consider the following piece of code
class ReadFrmFile {
public static void main(String args[]) {
FileInputStream fin = New FileInputStream("intemp.txt");
}
}
If the code is executed from C:\TEMP , the intemp.txt is expected to be in the working directory(C:/TEMP) in this case. If not this will throw the FileNotFoundException. The path of the file names are always absolute and not relative.
To avoid hardcoding the best way would be to place all the required files in the classpath and load them using getResourceAsStream().
Properties props = new Properties();
try {
InputStream inStream = **YOUR_CLASS_NAME**.class.getResourceAsStream("/log4j.properties");
props.load(inStream);
} catch (Exception e){
LOG.error(e);
}
PropertyConfigurator.configure(props);
log4j, by default, looks for the log4j.properties file in the classpath, so you don't need to use the class PropertyConfigurator, ony if the file doesn't exist in the root of the classpath.
In the Spring MVC + Log4j Integration Example, you will see:

How to build jar using Eclipse "Export as jar option" with a properties file

public xFbConfigReader()
{
//props = new Properties();
propsdatabase = new Properties();
try
{
// load a properties file
InputStream dbin = getClass().getResourceAsStream("/properties/database.properties");
propsdatabase.load(dbin);
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
I keep My properties File named 'Database.properties' in a folder where the project is named 'Properties'.
When I do a Export as jar in Eclipse . The Properties Folder is visible.
But When I run the program it shows that there is a NUll point exception in dbin.
So which means I require the proper way to form a jar in Eclipse .Kindly suggest.
The better solution while handling properties file would be reading
static Properties databaseproperties= new Properties();
static {
try {
connectionProps.load(YourClassName.class.getClassLoader()
.getResourceAsStream("databaseproperties.properties"));
} catch (Exception e) {
System.out.println("Exception is " + e.getMessage());
}
}
This is better approch because
we can move our properties file to someother folder.
And infact we can keep properties folder out side of jar. say you can create
a folder called Configuration where you can include all the
properties files. As it is out side of jar you can change the
properties file when ever is required.
For change in properties
file no need to unjar it.
(OR) simply you can make this change no need to think about directory structure
Step 1: Move properties file to SRC
step 2: change this line as
follows
InputStream dbin = getClass().getResourceAsStream("/database.properties");
This is not much different from previous code as it is anyway stays inside the JAR file.
you are getting null pointer exception because properties file is not loaded try to use
FileInputStream to load the properties as follows
FileInputStream dbin = new FileInputStream("/properties/database.properties");
properties.load(dbin);

Categories