I want to list all files with a specific name in the class path. I am expecting multiple occurrences, hence Class.getResource(String) will not work.
Basically I have to identify all files with a specific name (ex: xyz.properties) anywhere in the class path and then read the metadata in them cumulatively.
I want something of the effect Collection<URL> Class.getResources(String) but could not find anything similar.
PS: I don't have the luxury of using any third party libraries, hence in need of a home grown solution.
You can use Enumeration getResources(String name) on the class loader to achieve the same.
For example:
Enumeration<URL> enumer = Thread.currentThread().getContextClassLoader().getResources("/Path/To/xyz.properties");
while (enumer.hasMoreElements()) {
System.out.print(enumer.nextElement());
}
What I do is I read java source files from classpath and process them using ClassLoader. I am using follwing code :
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
assert (classLoader != null);
// pkgName = "com.comp.pkg"
String path = pkgName.replace('.', '/');
// resources will contain all java files and sub-packages
Enumeration<URL> resources = classLoader.getResources(path);
if(resources.hasMoreElements()) {
URL resource = resources.nextElement();
File directory = new File(resource.getFile());
// .. process file, check this directory for properties files
}
Hope this helps you.
The existing answers that propose using ClassLoader.getResources(String) only work if the "name" in question is the full path of the resource. As the methods's javadoc states, "The name of a resource is a /-separated path name that identifies the resource." It's unfortunate and misleading that the parameter is called "name" instead of "path."
For those who, like me, are really looking for all resources with a given (simple) name - without knowing the full path of the resource - the current answers won't work. #NandkumarTekale's code example states "resources will contain all java files and sub-packages" in the comments, but unfortunately that is not true. Nor does it make any difference to use ClassLoader.findResources(String) (which is indeed protected in the ClassLoader class itself, but pulled into the public API by concrete subclasses like URLClassLoader, which in turn serves as the base class for most commonly used class loaders).
The most straightforward solution that I could find uses the ClassGraph library:
try (ScanResult result = new ClassGraph().acceptClasspathElementsContainingResourcePath("*/html5.dtd").scan())
{
System.err.println(result.getResourcesWithLeafName("html5.dtd").getURLs());
}
Sadly, this clashes with the OP's request for a solution without the use of third-party libraries. However, ClassGraph's source code is available on GitHub and can be used as an inspiration for a "home grown" solution. In broad strokes, one would have to find the class loader's base URLs (e.g., using URLClassLoader.getURLs()) and then write URL-specific code for searching the contents of each URL (e.g., if it's a jar:file:... URL, load the JAR and iterate over its contents, or if it's a file:... URL use java.io.File to explore the folder contents). There are a lot of special cases to consider, though (e.g., OSGi class loaders), and a truly general solution would probably replicate a fair amount of what ClassGraph does under the hood. If it is valid to assume that the resources will, for example, always be packaged in a JAR (or always as plain files in the file system), a more tailored solution could be created for just those cases.
Related
This question already has answers here:
How to read several resource files with the same name from different JARs?
(2 answers)
Closed 4 months ago.
In my application, several libraries are assembled.
Whenever those libraries comes with resources having a same name, getResourceAsStream("myfile") seems to return a stream to whatever file come first.
This is true for any file, including Resource bundles.
In the following example, a library with a MyMibrary class has a file called "config.properties". And does
InputStream is = MyLibrary.class.getResourceAsStream("/config.properties")
When called in a standalone context, the right config file is used.
But when loaded as a dependency from an application having its own "config.properties", the same statement returns the config file of the application instead of the one of the library.
The same applies also for ResourceBundles (which is using getResourceAsStream too) :
If both the library and the application are using Resource bundles named the same way, when called from the application, the library will be using the application resource bundle instead of its own.
My question is how to instruct getResourceAsStream (especially in the context of Resource bundle loading) to use the right resource ?
EDIT: this question differs partially from How to read several resource files with the same name from different in the way the latter question deals only with resources while my question deals also with ResourceBundles.
you need getResources. Note the plural. Unfortunately, whilst MyLibrary.class.getResource is right, class does not have the getResources method copied over from ClassLoader. So, you do have to go via classloader. This is annoying, because in admittedly exotic (Agents, bootstrap classes) situations, MyClass.class.getClassLoader() will be null, so then you need to write code to defer to the system loader instead. For bonus annoyance, the method predates iterators, so it returns an Enumeration instead.
Thus, the full treatment:
ClassLoader cl = MyClass.class.getClassLoader();
if (cl == null) cl = ClassLoader.getSystemClassLoader();
var resources = cl.getResources("config.properties"); // No slash! See footnote 1
while (resources.hasNextElement()) {
URL resource = resources.nextElement();
try (var in = resource.openStream()) {
// yay we can read it now!
}
}
In admittedly really exotic context
[1] Loading via the classloader already starts at the classloader's root, so the leading slash is not required. In fact, if you use it, it'll fail. So, MyClass.class.getLoader().getResource("foo") is the same as MyClass.class.getResource("/foo"), except that the first snippet fails in agent/bootstrap situations.
So I have a project, and this is one of the demands:
You should have a class named Project3, containing a main method.
This program reads the levels information from a file whose name is
specified as a command-line parameter (The file should also be
relative to the class-path as described here:)
All the file names specified in the levels and block definition files
should be relative to the class path. The reason we want them to be
relative to the class path is that later we will be able to read the
files from inside a jar, something we can not do with regular File
references.
To get an input stream relative to the class path (even if it's inside
a jar), use the following:
InputStream is =
ClassLoader.getSystemClassLoader().getResourceAsStream("image.png");
The idea is to keep a folder with files(definitions and images) and
then add that folder to the class path when running the JVM:
java -cp bin:resources ... If you don't add the resources folder to
you class path you wont be able to load them with the command from
above.
When run without parameters, your program should read a default level
file, and run the game accordingly. The location of the default level
file should be hard-coded in your code, and be relative to the
classpath_.
When run without parameters, your program should read a default level file, and run the game accordingly. The location of the default level file should be hard-coded in your code, and be relative to the classpath_.
The part of the code that handles the input is:
public Void run() throws IOException {
LevelReader level = new LevelReader();
List<level> chosenLevels = new ArrayList<>();
if (args.length >= 1) {
File f = new File(args[0]);
if (f.exists()) {
chosenLevels = level.makeLevel(args[0]);
}
}
if (chosenLevels.size() == 0) {
game.runLevels(defaultLevels);
} else {
game.runLevels(chosenLevels);
}
return null;
}
So my question is:
An argument should be the full path of a file which means:
D:\desktop\level3.txt
Is it possible to read a file from every location on my computer?
Because right now I can do it only if my text file is in the
project's directory (not even in the src folder).
I can't understand the rest of their demands. What does is mean "should be hard-coded in your code, and be relative to the
classpath_." and why is it related to InputStream method(?)
I'm confused all over this.
Thanks.
A classpath resource is not the same as a file.
As you have correctly stated, the full path of a file is something like D:\desktop\level3.txt.
But if ever want to distribute your application so it can run on other computers, which probably won’t have that file in that location, you have two choices:
Ask the user to tell the program where to find the file on their computer.
Bundle the file with the compiled program.
If you place a non-.class file in the same place as .class files, it’s considered a resource. Since you don’t know at runtime where your program’s class files are located,¹ you use the getResource or getResourceAsStream method, which is specifically designed to look in the classpath.
The getResource* methods have the additional benefit that they will work both when you are developing, and when the program is packaged as a .jar file. Individual entries in a .jar file are not separate files and cannot be read using the File or FileInputStream classes.
If I understand your assignment correctly, the default level file should be an application resource, and the name of that resource is what should be hard-coded in your program. Something like:
InputStream is;
if (args.length > 0) {
is = new BufferedInputStream(
new FileInputStream(args[0]));
} else {
// No argument provided, so use program's default level data.
is = ClassLoader.getSystemClassLoader().getResourceAsStream("defaultlevel.txt");
}
chosenLevels = level.makeLevel(is);
¹ You may find some pages that claim you can determine the location of a running program’s code using getProtectionDomain().getCodeSource(), but getCodeSource() may return null, depending on the JVM and ClassLoader implementation, so this is not reliable.
To answer your first question, it doesn't seem like they're asking you to read from anywhere on disk, just from within your class path. So that seems fine.
The second question, "What does is mean 'should be hard-coded in your code, and be relative to the classpath'?". You are always going to have a default level file in your project directory. Define the path to this file as a String in your program and that requirement will be satisfied. It's related to the InputStream because the stream requires a location to read in from.
I'm working on an application that needs to be able to do some analysis on Java code; some through the text and some through reflection. The user should be able to input a directory and the program will pull all .java and .class files from it. I have no problem with the .java files but when I try getting the class of the .class file it doesn't work.
I've looked at using ClassLoader with URL. What I've been able to find so far has given me this
URL url = this.openFile().toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass(this.path);
return cls;
path is just a string containing the actual path of the .class file in question, e.g. Users/me/Documents/Application/out/production/MyPackage/MyClass.class. From what I understand from my own reading, this method ties me to knowing the package structure of the input, but in general I don't. All I have is the absolute path of the .class file. Is there a way, just using this path (or some simple transformation of it) that I can load into my program the actual MyClass class object and start doing reflection on it?
You have 2 options:
Use a byte code library to first read the file, so you can find out what the actual class name is.
E.g. in your example it is probably MyPackage.MyClass, but you can't know that from the fully qualified file name.
Implement your own ClassLoader subclass, and call defineClass(byte[] b, int off, int len).
Recommend using option 2.
When I create ImageIcon class objects I use the following code:
iconX = new ImageIcon (getClass().getResource("imageX.png"))
The above code works correctly either in an applet or a desktop app when the .png is in the same folder of the class.
The question is: how to avoid a NullPointerException when the .Png is in another folder? Or how load the image in the object ImageIcon when it is in a different location to the class?
I don't understand how this method works, if anyone can help me I appreciate it. Thanks!!
Take a look at this - Class#getResource(java.lang.String)
Please click the link above and read the docs and follow to understand what's going on.
It says -
If the name begins with a '/', then the absolute name of the resource is the portion of the name following the '/'.
and
Otherwise, the absolute name is of the following form:
modified_package_name/name
Where the modified_package_name is the package name of this object with '/' substituted for '.'.
So, if this object (where you call getResource) is in package /pkg1 (/ meaning pkg1 is right under the root of the classpath) and you used "imageX.png" then the result would be pkg1/imageX.png which is correct because that's where the image is located at.
But, if we moved the resource (imageX.png) to some other package /pkg2 and you called the method same way then the result would still be pkg1/imageX.png but this time it would be incorrect because the resource is actually located in /pkg2. That's when you end up with NPE.
It's good to explicitly specify the full path of the resource starting from the root of the classpath. (e.g. "/pkg/imageX.png").
Hope this helps.
Simply supply the path to the resource.
So, if you put the image in "/resources/images" within your Jar, you would simply use
iconX = new ImageIcon(getClass().getResource("/resources/images/imageX.png"))
Essentially what you're saying is, class loader, please search your class path for the following resource.
If the image is internal (you want a location relative to your project, or perhaps packaged into your jar), do what mad programmer said:
iconX = new ImageIcon(getClass().getResource("/path/imageX.png"))
The path is relative, so path/ will be a folder in the same folder as your project (or packaged into your jar).
If you want an external image, simply hand ImageIcon constructor the path (ex. "C:/.../file.png"). This isn't recommended though, as it's better to use it as a resource.
For more info on the ImageIcon constructor, see here. for more info on loading class resources, see here (Javadoc links)
In Java I can have multiple instances of the some resource in the class path, and can access them thus:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = loader.getResources("META-INF/services/myService.properties");
while(resources.hasMoreElements()){
URL resource = resources.nextElement();
// process each resource here
}
This allows use of service provider interfaces, where multiple implementations can be made available at runtime by dropping additional jars into the classpath.
Is there any equivalent to this in Java ME?
[UPDATE 2011-April-11]
If I could somehow get a list of jar files in the classpath, I could probably just pass the URLs to the Connector like this:
String [] jarFiles = ... // somehow I got this
for(int i = jarFiles.length-1; i >= 0; i--){
String url = "jar:file:"+jarFiles[i]+"!/META-INF/services/myService.properties";
InputStream in = Connector.openInputStream(url);
//process each resource here
}
Any idea how I might get all jar files in the classpath in J2ME?
After some research, it looks like the application must be contained within a single Jar file in a J2ME environment, so concepts like the classpath simply do not apply, meaning that this is not possible. All dependencies must be available at compile time.