JavaFX:Editable Configuration Files After Packaging - java

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.

Related

Java: Can't read a file in my project directory?

I'm building a Vaadin(basically Java that compiles to html/javascript) project and am trying to import a template(basically a HTML file). For all intents and purposes thought, I'm just importing a file as an input stream. Here is the offending code:
File file = new File("C:/JavaProjects/VaadinSpikeWorkspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/CISTVaadinClient/VAADIN/themes/layoutsinteractionDetailsTabLayout.html");
InputStream is = null;
CustomLayout custom = null;
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e1) {
System.out.println("mark 1");
e.printStackTrace();
}
try {
custom = new CustomLayout(is);
} catch (IOException e) {
System.out.println("mark 2");
e.printStackTrace();
}
What I'm doing:
Deploying the Vaadin project (basically a dynamic web project with a few extra .jars) to tomcat and accessing thVe aadin Project using my browser
What I'm seeing:
A blank screen in my browser
File not found exception (i.e "mark 1")
And as a result: IOException (i.e. "Mark 2")
What I've checked:
The file definitely does deploy to tomcat with the rest of the project
Outside of the webapps folder, the file i'm trying to import is available via the browser once deployed (i.e. Localhost/myProject/MyFile.html)
The Tomcat install is fine (It was a fresh install and works with this/other projects outside of this problem)
What I've tried
Using a relative URL, or just the name of the file (i.e. New File( "../webapps/vaadin/layouts/MyFile.html") )
Using the absolute Path to the Project directory
Using the absolute path to the deploy directory (as above)
Putting the file somewhere else (read: Every single possible location in the project)
Again, I'm trying to simply read the file, MyFile.html as an input stream. What am I doing wrong/ overlooking?
Thanks for your time.
I had no problems reading files when using VaadinService which points to WebContent directory (with META-INF, VAADIN and WEB-INF inside). If it's run in the test environment then VaadinService is not available, so I use such piece of code:
private static final String BASEDIR;
static {
if (VaadinService.getCurrent() != null) {
BASEDIR = VaadinService.getCurrent().getBaseDirectory().getAbsolutePath();
} else {
BASEDIR = "WebContent";
}
}
then to navigate to VAADIN folder just use
BASEDIR+="/VAADIN/restOfYourPath"
Just in order to make it more portable: have you thought about bringing your templates into your classpath? Something like
yourApp/WEB-INF/classes/templates/layoutsinteractionDetailsTabLayout.html
This way, you only need this line of code, assuming you are into a Servlet or Spring Controller or Struts 1/2 action or whatever called YourClass:
InputStream is = YourClass.class.getClassLoader().getResourceAsStream("templates/layoutsinteractionDetailsTabLayout.html");
If you are really trying to use this file as an HTML template, you'll be much better off to leverage Vaadin's support for this. They have a CustomLayout which loads an HTML template from your theme.
Your template would go into a folder like the following:
VAADIN/themes/mytheme/layout/layoutsinteractionDetailsTabLayout.html
Note that mytheme is the name of your theme and layout is a specially recognized Vaadin directory within themes.
Your custom component would then look like:
public class InteractionDetailsTabLayout extends CustomLayout {
private static final String TEMPLATE = "layoutsinteractionDetailsTabLayout";
public InteractionDetailsTabLayout() {
super(TEMPLATE);
}
}
Note that the super constructor argument excludes the directory and file suffix.
If you actually want to load a file in your webapp, don't bother with it in your VAADIN directory but instead put it in your classpath resources and access it with the ClassLoader.

File not found exception with external files

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.!!

Find relative path of java application

I have read all the other questions related to this in StackOverflow and I did not find any clear response.
To cut it short, I have an application that will store some files in a directory that I will use than to process them. I have intentions of moving my app in different places (other computers) so I need to have a relative path to work with so that I will not change that in each time.
Does anyone know how to get the relative path of the application (not the full path) so that I could use in this case? If what I am asking is not wright please tell me another way to achieve what I need. Thank you
Just use "./".
No matter what directory your application has been launched from, "./" will always return that directory.
For example:
new File("./") will return a file object pointed at the directory your java application has been launched from
new File("./myDirectory") will return a file object pointed at the myDirectory folder located in the directory your java application has been launched from
Here is one approach:
I believe you need to define the path of directory containing the files in a configuration/property file. You can change the path in the configuration file when you move your application or the directory containing the file. This is how your properties file(let's say config.properties) contents should be:
filesDirPath=\usr\home\test
And this what you should do in the code:
private void readConfig()
{
Properties prop = new Properties();
try {
//load a properties file
prop.load(new FileInputStream("config.properties"));
//get the directory path property value
String flesDirPath = prop.getProperty("filesDirPath");
System.out.println("Files to be read are located in dir : " + flesDirPath );
} catch (IOException ex) {
ex.printStackTrace();
}
}

How to call database.properties outside the project using notepad in java?

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

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