Using File in Java [duplicate] - java

I've created a jar-File with maven. When i open this jar i can find the following content:
my.jar
|_text1.txt
|_folder
|_ ... some other stuff
When i'm running this code snippet in Eclipse, content of "text1.txt" and all filenames from folder content where printed out
String tmp = IOUtils.toString(App.class.getClassLoader().getResourceAsStream("text1.txt"), StandardCharsets.UTF_8);
System.err.println(tmp);
tmp = IOUtils.toString(App.class.getClassLoader().getResourceAsStream("folder"), StandardCharsets.UTF_8);
System.err.println(tmp);
When i'm running this code as a standalone program, content from "text1.txt" will be printed as well, but the second part for folders returns null.
What am i doing wrong?

App.class.getClassLoader().getResourceAsStream
Don't do this. The right way is App.class.getResourceAsStream. There are times when getCLassLoader() returns null; in such cases, your strategy is broken, the above will work fine. Also, your way is more calls and more code for no gain.
IOUtils.toString(App.class.getClassLoader().getResourceAsStream("folder")
You can't get a 'folder'. What do you think this would even do? The abstraction designed into the resource loader system (which is what you're using here) doesn't let you get folders in any way, and does not allow you to list the contents of a folder either. (You're possibly looking for SPI: Service Provider Interface).
IOUtils
You do not need this; java.nio.file.Files is built-in, and has a toString method just the same. It even defaults to UTF-8 so you don't have to specify it.
App.class.getResourceAsStream("text1.txt")
This will look for t1.txt in the exact same place as App.class. Even the same package structure. If you want to go from the 'root' of where App.class is found (so, the thing on your -classpath, generally), put a slash in front: Ask for "/text1.txt" for example).
Given that you have text1.txt in the root, and not in com/foo/pkg next to com/foo/pkg/MyApp.class, you'd need to specify "/text1.txt" to find it.
If you're unsure about where this stuff is looking, sysout the result of this call:
App.class.getResource("App.class");
this prints a URL and from it your eyeballs will be able to tell where it's looking.
and all filenames from folder content where printed out
That's nice. This does not work - that eclipse gives you this at runtime is weirdness baked in, but the spec fundamentally has no abstraction for this. ClassLoader implementations can do whatever they want (it's a pluggable system) and the only method they need implement is 'find the resource at this location'. There is no obligation to return a string of filenames if you ask for a 'folder', and most classloaders (including the one reading jar files) simply don't.
The solution is SPI: At compile time make a file that lists paths or classnames, and then at runtime use the resource system to load the file, and then load every class / every resource listed inside it. An annotation processor can automate the process of generating this file as part of compilation, making the whole ordeal seamless. Search the web for SPI java for more.

Related

How does ClassLoader.getResourceAsStream work?

I've created a jar-File with maven. When i open this jar i can find the following content:
my.jar
|_text1.txt
|_folder
|_ ... some other stuff
When i'm running this code snippet in Eclipse, content of "text1.txt" and all filenames from folder content where printed out
String tmp = IOUtils.toString(App.class.getClassLoader().getResourceAsStream("text1.txt"), StandardCharsets.UTF_8);
System.err.println(tmp);
tmp = IOUtils.toString(App.class.getClassLoader().getResourceAsStream("folder"), StandardCharsets.UTF_8);
System.err.println(tmp);
When i'm running this code as a standalone program, content from "text1.txt" will be printed as well, but the second part for folders returns null.
What am i doing wrong?
App.class.getClassLoader().getResourceAsStream
Don't do this. The right way is App.class.getResourceAsStream. There are times when getCLassLoader() returns null; in such cases, your strategy is broken, the above will work fine. Also, your way is more calls and more code for no gain.
IOUtils.toString(App.class.getClassLoader().getResourceAsStream("folder")
You can't get a 'folder'. What do you think this would even do? The abstraction designed into the resource loader system (which is what you're using here) doesn't let you get folders in any way, and does not allow you to list the contents of a folder either. (You're possibly looking for SPI: Service Provider Interface).
IOUtils
You do not need this; java.nio.file.Files is built-in, and has a toString method just the same. It even defaults to UTF-8 so you don't have to specify it.
App.class.getResourceAsStream("text1.txt")
This will look for t1.txt in the exact same place as App.class. Even the same package structure. If you want to go from the 'root' of where App.class is found (so, the thing on your -classpath, generally), put a slash in front: Ask for "/text1.txt" for example).
Given that you have text1.txt in the root, and not in com/foo/pkg next to com/foo/pkg/MyApp.class, you'd need to specify "/text1.txt" to find it.
If you're unsure about where this stuff is looking, sysout the result of this call:
App.class.getResource("App.class");
this prints a URL and from it your eyeballs will be able to tell where it's looking.
and all filenames from folder content where printed out
That's nice. This does not work - that eclipse gives you this at runtime is weirdness baked in, but the spec fundamentally has no abstraction for this. ClassLoader implementations can do whatever they want (it's a pluggable system) and the only method they need implement is 'find the resource at this location'. There is no obligation to return a string of filenames if you ask for a 'folder', and most classloaders (including the one reading jar files) simply don't.
The solution is SPI: At compile time make a file that lists paths or classnames, and then at runtime use the resource system to load the file, and then load every class / every resource listed inside it. An annotation processor can automate the process of generating this file as part of compilation, making the whole ordeal seamless. Search the web for SPI java for more.

How to correctly setup resources in a exportable library

In a library I made, I have the following line to retrieve my resources:
private static final File fragment = new File(DefaultShader.class.getClassLoader().getResource("file.txt").getFile());
But when I export this library to a JAR and attempt to use it in another application, when this same line is read internally I get an I/O error because it's trying to access the JAR.
java.io.FileNotFoundException: file:\C:\Users\me\test\libs\lib.jar!\file.txt
Nodejs would make this easier by providing methods that returns the runtime location path of a script, apparently there is not an equivalent in Java or just couldn't find it. How can I get around this issue?
Never call the getFile method of URL. It does not return a valid file name. It only returns the path portion of a URL.
A resource embedded in a .jar file is not a File and you cannot refer to it as a File.
Fortunately, you don’t need a File object. You can read it directly using getResourceAsStream:
DefaultShader.class.getResourceAsStream("/file.txt")
Obviously, you should not store an InputStream in a static final field. But you can easily make a method instead:
private static InputStream readFileData() {
return DefaultShader.class.getResourceAsStream("/file.txt");
}
Be aware that resources should be placed in packages, just like classes. Placing a resource in the root of a .jar is like writing a file to a drive’s root directory or the user’s home directory: if any other program chooses to use the same filename, the results for both programs will be problematic, to say the least.
Similarly, if your resource is in the root of your .jar, and another library also happens to store a resource with the same name in the root of its own .jar, there will be a conflict, and it may or may not be your resource which gets loaded by the getResource* methods, depending on the current classpath definition. (This concern doesn’t apply to Java 9+ modular programs, but it’s still a good idea to keep resources in packages.)
The practice of putting a resource in the same package as the class that uses it is considered a good practice by Java SE: the getResource and getResourceAsStream methods are designed to expect it in the same package as the class by default. If the string argument does not start with a slash, those methods assume it’s in the same package.
This looks for file.txt in the same package as the DefaultShader class:
DefaultShader.class.getResourceAsStream("file.txt")
Whereas this will look for file.txt in the root of every .jar in the classpath:
DefaultShader.class.getResourceAsStream("/file.txt")

tuProlog - using multiple files with consult - can't get engine to load additional files with consult(otherFile.pl)

Edit: Clarity - the main .pl file loads, it's all the subfiles that it has been told to load which don't load. (all the consult('subfile.pl').)
I have a Java project that uses tuProlog. It calls a theory as:
Theory theory = new Theory(":-consult('main.pl').");
engine.setTheory(theory);
This is as per the manual.
file.pl exists in the same folder as other prolog files.
Inside of main.pl, I have further
consult('otherfile.pl').
statements to load additional files (several).
The folder structure is:
src/main.pl
src/Prolog_Files/otherfile.pl (multiple)
src/main/java/JavaStuff
I can't get the engine to load the theories that I've told it to consult inside of the main file.pl
I have tried: giving it absolute path instead of just filename.
moving files around.
I'm wondering if there is something about usage of tuProlog I'm not understanding?
The theory works when loaded with:
Theory theory = new Theory(new FileInputStream(url_of_file)).
However, this is causing me issues when building the jar, as it can't find the file location.
Am I attempting to load the file correctly? Are my consults inside of the main .pl file correct?
Could someone please post an example of how this should be done if not? The manual doesn't elaborate very much on this topic.
Thanks
The manual is slightly outdated in parts - it says to use consult/1, whereas elsewhere it states that consult/1 is deprecated, whereas include/1 is the replacement.
Secondly, when using 2p.jar, it reads Prolog files from the Project root as its root. When creating a jar, 2p.jar cannot be inside of the project jar. They should be in relative folders, and 2p.jar reads Prolog files with the location of 2p.jar as root. It doesn't seem that it can read inside of project jar.
Hopefully that's clear enough!

In an Eclipse plugin, what's the right way to get the .class file that corresponds to the selected java file?

I'm writing an Eclipse plugin. I'm looking for the right way to get a selected java file and find it's corresponding .class file.
The way I'm doing it now works for a standard Eclipse java project, but seems less than elegant: I call IJavaProject#getOutputLocation and combine that with the path to the selected file. Then I just change .java to .class.
Here's the code for the method that translates from the incoming selection to a path:
def selection_to_path selection
path = selection.get_paths.first
output_location = path.get_first_segment.get_output_location
root = org.eclipse.core.resources::ResourcesPlugin.getWorkspace().get_root
folder = root.get_folder output_location
folder.get_location.to_s
path_to_file_as_string = path.get_last_segment.get_path.to_s
path_to_file_as_string[/\.java$/] = ".class"
path_elements_of_java_file = path_to_file_as_string.split("/")[3..-1]
all_elements = folder.get_location.to_s.split("/") + path_elements_of_java_file
File.join(all_elements)
end
This works, but I suspect it fails often enough that it's not the right thing to do. In particular, the documentation for getOutputLocation says that my assumptions about class file locations are too simplistic.
So what's the right way to do this?
I don't need this to work all of the time. Certainly there will be many cases where a .java file doesn't have a corresponding .class file (compilation errors, no automatic builds, whatever). I don't care about those; I just want to know if there is a class file, what is its full path?
There is no method, which directly finds the corresponding class file resource. The proper way to get the corresponding output folder is to:
Convert the selection to IJavaElement, most probably ICompilationUnit (.java files)
Find the corresponding IPackageFragmentRoot (the source folder)
Find the resolved IClasspathEntry
Find the output location using IClasspathEntry.getOutputLocation() or the IJavaProject.getOutputLocation() method, depending on whether the classpath entry has separate output location.
Construct the path to the classpath based on the ICompilationUnit's package
The class file may or may not exist depending on whether the project has been build, whether there are build path errors, etc. So, there are additional heuristics, which you need to use to determine whether the result of the above algorithm will be usable.

Does Java getResource method only work with particular extensions?

The following piece of code works fine
getClass().getResource("/index.xml");
However when I do a full refactor to
getClass().getResource("/index.html");
The above line throws NullPointerException. I know I have refactored correctly because I rename the file using IDE smart refactor i.e. the file index.html definitely exists in the same directory. As soon as I switch back to
getClass().getResource("/index.xml");
Everything is fine again. Is there any reason why only the .xml extension works?
As #a_horse_with_no_name mentions, using getResourceAsStream( ) should work fine with any file and any extension.
I'd be inclined to believe (based on the information presented) that your IDE hasn't properly refreshed its file hierarchy after the refactor. I'd suggest running a full clean and build of your project, see if that helps the situation.
So, most of the other answers, the class/class loader shouldn't be looking at file extension You could write a ClassLoader which did that, but it would be odd.
I'm going to take a stab at what your problem is. I am guessing using some IDE (you don't specify which) that is copying certain files from your source folder into the destination (either a jar or a directory of classes and resources). For Java code, you want the compiled .class object files there and not the .java sources. So the IDE will be configured, with some reasonable default [magic], to copy files with only certain extensions. HTML files were used for old package JavaDocs (package-info.html rather than package-info.java which can include package-wide annotations), so are arguably reasonable to exclude by default.
Therefore, you should investigate what the project is doing in this area, and change any configurations accordingly.
Using getResourceAsStream() should work with any file extension (at least it does for me)
Recognised Java Resources are either a class extending ResourceBundle or .property file.
You can write your own extenstions to enable Resource to be gathered from other extensions.
I'm unsure why .xml files are a viable extension. Are you using Java 7?
From the JavaDoc guide:
The name of a resource is independent of the Java implementation; in
particular, the path separator is always a slash (/). However, the
Java implementation controls the details of how the contents of the
resource are mapped into a file, database, or other object containing
the actual resource.
A resource is identified by a string consisting of a sequence of
substrings, delimited by slashes (/), followed by a resource name.
Each substring must be a valid Java identifier. The resource name is
of the form shortName or shortName.extension. Both shortName and
extension must be Java identifiers.
And read this doc which tell us : an absolute resource name is constructed from the given resource name using this algorithm.
getResource() will work regardless of the type of resource. All it does it return a URL. This example works fine for me.
public class Example {
public static void main(String... args) {
System.out.println(Example.class.getResource("jaxb.properties"));
System.out.println(Example.class.getResource("test.xml"));
System.out.println(Example.class.getResource("foo.html"));
System.out.println(Example.class.getResource("danger.exe"));
}
}

Categories