Add properties file when deploying - java

I am building a web application using IntelliJ 13 Ultimate and a tomcat8 server.
I have a properties file next to my TextUtil class in order to access it with
TextUtil.class.getResource(TEXT_PROPERTY_PATH).getFile()
But when I am starting my server with IntelliJ, by default the file is not being copied (which is quite logical, because it is not part of the compiler output).
I edited the artifact and added the files manually. But by doing this, the file is not being updated on change without restarting the server.
Is there
A) a better place for the properties-file from where I can access it withou having the context-object or
B) a way to leave the file where it is and get it updated on change?

You can add the file in the classpath and use the below code to have the inputstream
For example in ABC.properties or within a folder like properties/ABC.properties
NOTE:
After proper build it should go to the path /WEB-INF/classes//ABC.properties
public class TextUtil {
....................
//For ABC.properties
InputStream inStream1 = TextUtil.getClass()
.getClassLoader().getResourceAsStream("ABC.properties");
//For properties/ABC.properties
InputStream inStream2 = TextUtil.getClass()
.getClassLoader().getResourceAsStream("properties/ABC.properties");
....................
}

Related

Java read and write to the resource folder

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.

How to access a file while writing a Java external library

I am writing a small Java Library (say project A) to be used externally (as a .JAR) in any other project (project B).
This is how project A looks like :
projectA
--src/main/java
--packageOne
....
--packageTwo
--A.java // need to access the next few text files in this java file
--ImportantTextOne.txt
--ImportantTextTwo.txt
--ImportantTextThree.txt
This is how project B will look like :
projectB
--src/main/java
--B.java // I will use project A here.
I have tried importing the text files, but every time I use it as a .JAR externally in ProjectB, I always get some errors such as
java.nio.file.NoSuchFileException
I presume this is because of some class path issue.
So how do i correctly read my text files in ProjectA?
thanks in advance
Edit : I don't need the text files in projectB, they are just used once to pull text from in projectA. All I want is to correctly read those files in projectA, so I can import projectA in any project and not get errors.
You placed the txt files besides the class files. You may have to move them to
src/main/resources/packageTwo
for your build process to correctly handle them. Anyway, make sure they are at the right location once the jar file is built.
To access such a file, you cannot load it from the filesystem as it is part of your jar. But it is on the classpath. So you need to access it like
URL url = A.class.getResource("/packageTwo/ImportantTextOne.txt");
// check what url you got - if it is null the resource was not found
InputStream in = url.openStream();
...
So with the help of Hiran's response and digging around (also this) I figured it out.
File structure of the library you are writing :
projectA
--src/main/java
--packageOne
....
--packageTwo
--A.java // need to access the next few text files in this java file
--ImportantTextOne.txt
--ImportantTextTwo.txt
--ImportantTextThree.txt
Whenever reading a file, treat it as a resource. As when the class will be used as an external .JAR you will not be able to access files inside the .JAR. Instead follow this type of pattern :
A.java
URL url = this.getClass().getResource("ImportantTextOne.txt") // this assumes your text file is in the same location as A.java
InputStream in = url.openStream();
String text = new BufferedReader(
new InputStreamReader(in, StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));

How can I make a file visible both when project is deployed on a server and when its classes are run from test classes?

If I use a file calling it directly:
FileInputStream fileInputStream = new FileInputStream("SR02_pattern.xls");
( the file is in \apv\main-app directory), it won't be deployed, and of course, it won't be seen when the project will be run on the server.
If I put the file in the /apv/main-web/WEB-INF/classes/ directory, it will be deployed and I can call it by
InputStream inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("SR02_pattern.xls")
when the project is deployed on the server, but that line won't read the file in the case the class was run from the test.
Probably, the place where the file will be looked for by getResourceAsStream("SR02_pattern.xls"), is set by some system properties and I can use them, but I don't know which properties can help.
How can I read the file in both cases by the same code without passing it as a parameter into the class?
There are many answers on SO for either of those cases, but I couldn't find one that works for both. The default paths for both cases are different.
Of course, I can put the file in both places, and in case the file is not in the first folder, look it in the second, as I am doing now, but upkeeping two copies is prone to errors and I desire to use better style.
You should use this.getClass().getResourceAsStream('/SR02-pattern.xl') for all parts in test as well in production code. The / defines the root directory for the resources.
The files you would like to read should be located in src/main/resources. This will result in the final war package at the location WEB-INF/classes.

Opening a xml file from eclipse and from a .jar file in java

Yesterday, I had a problem because I couldn't manage to open a xml file (it owuld give me a FileNotFoundException) located in the ressources folder of my .jar file, which I managed to open on eclipse using the following lines of code. You can see my old problem here. This was my code with the problem :
File xmlFile = new File("ressources/emitter.xml");
ConfigurableEmitter emitter = ParticleIO.loadEmitter(xmlFile);
Someone told me it that one way was to use getClassLoader().getRessourceAsStream method to open a xml file in a .jar file that was exported
InputStream i= this.getClass().getClassLoader().getResourceAsStream("ressources/emitter.xml");
ConfigurableEmitter emitter = ParticleIO.loadEmitter(i);
Unfortunately, that solution only works when I export my project into a .jar file, so if I want to go back debugging my program, I have to take the old code that would only works on eclipse.
My question is: is there any better way to do this without having to change my code if I want to export it or if I want to debug it?
Thank you
edit :
Thank you all, it works perfectly fine now
my problem was that I put my ressources folder like that :
+project
+src
+ressources
+emitter.xml
InputStream i= this.getClass().getClassLoader().getResourceAsStream("/ressources/emitter.xml");
The above should work in both cases (Note is is /resources/.... This is assuming say your directory structure is below:
MyProject
+src
+ressources
emitter.xml
Place the file alongside your source files, then you can use the getResourceAsStream() method in both cases. Don't forget to update the path (which should be the package name of your class, but with slashes instead of dots).
My question is: is there any better way to do this without having to
change my code if I want to export it or if I want to debug it?
Yes, use Maven. Maven will handle that and it hooks into Eclipse beautifully (NetBeans too!) What you do is place the resource in src/main/resources and then you can have Eclipse run the test goal of the Maven project or you can just run mvn test from the command line. Another advantage of using Maven here is that you can also have src/test/resources/emitter.xml which overrides the one in src/main with environment-specific test instructions and it won't affect your deployment.
InputStream i= getClass().getClassLoader().getResourceAsStream("ressources/emitter.xml");
or
InputStream i= getClass().getResourceAsStream("/ressources/emitter.xml");
(note the absolute positioning)
both work when the class is in the same jar, on the same class path.
In the jar the names must be case sensitive, but as the jar already works. Ensure that the ressources directory is on the class path too, or copied to the target directory.
As "ressources" is probably configured yourself (not named "resources" as in English), you probably need to add it to the build somehow.

properties file not working inside jar

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.

Categories

Resources