I have a java program where I wrote a scheduler, or a timer where once it starts running. It will call the function every N minutes interval indefinitely. For now, I set N to 5 minutes.
I want to sent out this program in a form of jar file along with some sort of properties file where the tester or anyone else can configure N; so it's much more convenient and I do not have to change in the code itself.
This is an example:
public void schedule() throws Exception {
Timer t=new Timer();
t.scheduleAtFixedRate(
new TimerTask() {
#Override
public void run() {
// System.out.println("HELEOELE");
try {
test.index();
} catch (Exception e) {
e.printStackTrace();
}
}
},
0,
300000);
}
In this case, N = 300000.
I am not too familiar with jar file but I do have a properties file with db connection created.
This is how I called the properties:
Properties props=new Properties();
InputStream in = getClass().getResourceAsStream("/config.properties");
props.load(in);
in.close();
int value=Integer.parseInt(props.getProperty("value"));
So I would appreciate is there any way to do this?
You can read the properties file from the classpath in case it is present in your jar only.
Refer this:
Load properties file in JAR?
But, if you wan't to store the properties file out of the jar and be able to make changes in it, then there are multiple options :
store it on the file system at a specified location from where your code reads it.
instead of a properties file, you can also have a system property defined, which stores the required value. But, you would have to restart your container every time you change the value.
you could also consider reading this value directly from the database (optionally loading it into an in-memory cache). But, here, another API would needed to make changes in the database.
Store it outside the jar. Refer the following link
Read properties file outside JAR file
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
I have written a program to process files in a directory. At start up it reads the current files in a directory, and then it uses a monitor to discover new files. Once it has processed a file,the program deletes the file. The problem is that there is a time gap, no matter how slight, between reading the files in a directory at startup and then starting the listener. A file created in that gap would be missed. One possible solution would be to repeatedly read the files in a directory (newDirectoryStream), but that doesn't seem as elegant or possibly efficient as using a monitor. The code uses the Apache Commons monitor and looks something like:
// Read Current files
stream = Files.newDirectoryStream(listenDir);
processFile(file);
// Process New files
FileAlterationObserver observer = new
FileAlterationObserver(listenDir.toAbsolutePath().toString(),filter);
FileAlterationMonitor monitor = new FileAlterationMonitor(POLL_INTERVAL);
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
#Override
public void onFileCreate(File file) {
processFile( file.toPath());
}
};
observer.addListener(listener);
monitor.addObserver(observer);
monitor.start();
Simply flip it: First set up the listener and then obtain a directory stream. Go through a concurrent set which lets you do a 'only once ever' layout (the one that added the file name to the set and got the return value indicating 'you actually are the one that added it, you're not merely re-applying something that was already in there' - then you handle the file, otherwise you keep going). This way, if the file is added right in the 'sweet spot', both the dirstream and the observer would get it, but still only one will process it.
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.!!
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.
I want to take place database.properties outside the project, so when I want to change the content (database configuration) of that when I've build them into jar, I can do it easily without open my project again. So what to do?
First, place the database.properties file in the location you'd like it to be in.
Then, do one of the following:
Add the directory where database.properties is located, to the classpath. Then use Thread.currentThread().getContextClassLoader().getResource() to get a URL to the file, or getResourceAsStream() to get an input stream for the file.
If you don't mind your Java application knowing the exact location of the database.properties file, you can use simple File I/O to obtain a reference to the file (use new File(filename)).
Usually, you'd want to stick with the first option. Place the file anywhere, and add the directory to the classpath. That way, your Java application doesn't have to be aware of the exact location of the file - it will find it as long as the file's directory is added to the runtime classpath.
Example (for the first approach):
public static void main(String []args) throws Exception {
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("database.properties");
Properties props = new Properties();
try {
// Read the properties.
props.load(stream);
} finally {
// Don't forget to close the stream, whatever happens.
stream.close();
}
// When reaching this point, 'props' has your database properties.
}
Store properties file in your preferred location. Then do the following:
try {
String myPropertiesFilePath = "D:\\configuration.properties"; // path to your properties file
File myPropFile = new File(myPropertiesFilePath); // open the file
Properties theConfiguration = new Properties();
theConfiguration.load(new FileInputStream(myPropFile)); // load the properties
catch (Exception e) {
}
Now you can easily get properties as String from the file:
String datasourceContext = theConfiguration.getString("demo.datasource.context", "jdbc/demo-DS"); // second one is the default value, in case there is no property defined in the file
Your configuration.properties file might look something like this:
demo.datasource.context=jdbc/demo-DS
demo.datasource.password=123