I'm trying to load a resource (plain text file) from a second JAR that has not be loaded yet. This resource will contain a string representing a class in this second jar which I plan to use.
I'm having trouble finding the correct way to load this resource, and previous similar questions haven't gotten me much further. Here is what I'm working with:
public void readResource() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
}
I can see in this ClassLoader (which ends up being a WebappClassLoader) has the list of jars in the directory:
jarNames: [com.mysql.jdbc.jar, productivity-common.jar]
jarPath: /WEB-INF/lib
When I try to load up the file using the ClassLoader, I'm getting a NullPointerException:
String path = loader.getResource("com/productivity/common/META-INF/providers/hello.txt").getPath();
If this would work, my next step would be reading the value in this file using an InputStream, and trying to create a new instance of a class matching that value from the same second jar. From what I'm reading, I would use the path to that class and use Class.forName("value").newInstance(), but I'm not confident that's right either.
Any assistance would be greatly appreciated. I'm trying to learn how ClassLoaders work and writing this (what should be simple) project to help.
Let me assume you have two resource files with same name "spring/label.properties" stored in two different jar files.
You can use following code to find list of all files from class path, then filter based on path.
Enumeration<URL> en = this.getClass().getClassLoader().getResources("spring/label.properties");
while(en.hasMoreElements()){
URL url = en.nextElement();
//Print all path to visualize the path
System.out.println(url.getPath());
if(url.getPath().contains("my-jar")){ // This can be jar name
BufferedReader reader = new BufferedReader(new InputStreamReader(en.nextElement().openStream()));
String str = null;
while((str = reader.readLine())!=null){
// Now you can do anything with the content.
System.out.println(str);
}
}
}
Does that help?
Related
I'm just trying to read in a simple .txt file into my java project using this.class.getResourceAsStream(filename). I have several files within main/resources, and almost all of them return an object when I try to get them as an input stream. The only object I can't read in is my text file.
I have placed the file with all of the other resource files that are readable by the classloader, but it appears this file wasn't placed in the class' classLoader for whatever reason. If I unzip the jar, the file is still included with the jar in the same directory as all of the other resources, so it seems to be being built correctly.
I guess what I'm asking is at what point do I tell Java what files I want to be included as a resource in a class' ClassLoader? Is it something that should be done when the jar is built if things are in the correct place (i.e main/resources)?
Here is what the code looks like, and it's respective return values, when running for the file it can find and the file it can't, that are both located in the same place.
// This is not found. Both are placed at src/main/resources
def tmpDict = this.class.getResourceAsStream("dict.txt")
println tmpDict // null
// This is found
def tmpDict2 = this.class.getResourceAsStream("calc.config")
println tmpDict2 // sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream#2dae5a79
Without more info i'd say the path is wrong. when i used just "file.txt" for the path it got NPE
i used this method to read from the stream. The file was located at \src\main\resources\static\file.txt
This worked in eclipse, packaged into jar and worked there too.
public String getFile() throws Exception {
InputStream in = Controller.class.getClassLoader().getResourceAsStream("static/file.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
}
return out.toString();
}
Some very Basic checks:
The file must be case-sensitive (as it is not a Windows file), and special characters of the file name might be cumbersome. Also check that the file in your project has the file extension not twice (.txt.txt - Windows hiding the extension).
Check that getResourceAsStream("/a/b/c/A.txt") indeed gives a null.
If not the reading might go wrong on the encoding.
I have jar file langdetect.jar.
It has a hierarchy shown in image
There is a class LanguageDetection at com/langdetect package.
I need to access the path of the profiles.sm folder from above class while executing the jar file.
Thanks in advance.
Jars are nothing else than Zip files and Java provides support for handling those.
Java 6 (and earlier)
You can open the jar file as a ZipFile and iterate over the entries of it. Each entry has a full path name inside the file, there is no such thing as relative path names. Though you have to take care, that all entries - although being absolute in the zip file - do not start with a '/', if you need this, you have to add it. The following snippet will get you the path of a class file. The className has to end with .class, i.e. LanguageDetection.class
String getPath(String jar, String className) throws IOException {
final ZipFile zf = new ZipFile(jar);
try {
for (ZipEntry ze : Collections.list(zf.entries())) {
final String path = ze.getName();
if (path.endsWith(className)) {
final StringBuilder buf = new StringBuilder(path);
buf.delete(path.lastIndexOf('/'), path.length()); //removes the name of the class to get the path only
if (!path.startsWith("/")) { //you may omit this part if leading / is not required
buf.insert(0, '/');
}
return buf.toString();
}
}
} finally {
zf.close();
}
return null;
}
Java 7/8
You may open the JAR file using the Java7 FileSystem support for JAR files. This allows you to operate on the jar file as if it would be normal FileSystem. So you could walk the fileTree until you have found your file and the get the Path from it. The following example uses Java8 Streams and Lambdas, a version for Java7 could be derived from this but would be a bit larger.
Path jarFile = ...;
Map<String, String> env = new HashMap<String, String>() {{
put("create", "false");
}};
try(FileSystem zipFs = newFileSystem(URI.create("jar:" + jarFileFile.toUri()), env)) {
Optional<Path> path = Files.walk(zipFs.getPath("/"))
.filter(p -> p.getFileName().toString().startsWith("LanguageDetection"))
.map(Path::getParent)
.findFirst();
path.ifPresent(System.out::println);
}
Your particular Problem
The above solutions are for finding the path inside a Jar or Zip, but may possibly not be the solution to your problem.
Im not sure, whether I understand your problem correctly. As far as I see it, you'd like to have access to the path inside the classfolder for any purpose. The problem with that is, that the Class/Resource lookup mechanism doesn't apply to folders, only files. The concept that is close is a package, but that is always bound to a class.
So you always need a concrete file to be accessed via getResource() method. For example MyClass.class.getResource(/path/to/resource.txt).
If the resources are located in a profiles.sm folder relative to a class and its package, i.e. in /com/languagedetect/profile.sm/ you could build the path from the reference class, for example the class LanguageDetection in that package and derive the absolute path from this to the profiles.sm path:
String basePath = "/" + LanguageDetection.class.getPackage().getName().replaceAll("\\.", "/") + "/profiles.sm/";
URL resource = LanguageDetection.class.getResource(basePath + "myResource.txt");
If there is only one profiles.sm in the root of the jar, simply go for
String basePath = "/profiles.sm/";
URL resource = LanguageDetection.class.getResource(basePath + "myResource.txt");
If you have multiple jars with a resource in /profiles.sm, you could gain access to all of those via the classloader and then extract the Jar file from the URL of the class
for(URL u : Collections.list(LanguageDetection.class.getClassLoader().getResources("/profiles.sm/yourResource"))){
System.out.println(u);
}
In any case it's not possible without accessing the zip/jar file to browse the contents of this path or folder because Java does not support browsing for classes or resources inside a package/folder in classpath. You may use the Reflections lib for that or extend the ClassLoader example above by additionally reading the content of the detected jars using the zip example from above.
In my application I load resources in this manner:
WinProcessor.class.getResource("repository").toString();
and this gives me:
file:/root/app/repository (and I replace "file:" with empty string)
This works fine when I run my application from the IDE, but when I run the jar of my application:
java -jar app.jar
The path becomes:
jar:/root/app.jar!/repository
is there any way to solve this problem?
I'll use the "repository" dir name in order to create this:
ConfigurationContext ctx = (ConfigurationContext) ConfigurationContextFactory.createConfigurationContextFromFileSystem(repositoryString, null);
In the same manner, I'll get one file name (instead of a dir) and I'll use it this way:
System.setProperty("javax.net.ssl.trustStore", fileNameString)
It sounds like you're then trying to load the resource using a FileInputStream or something like that. Don't do that: instead of calling getResource, call getResourceAsStream and read the data from that.
(You could load the resources from the URL instead, but calling getResourceAsStream is a bit more convenient.)
EDIT: Having seen your updated answer, it seems other bits of code rely on the data being in a physical single file in the file system. The answer is therefore not to bundle it in a jar file in the first place. You could check whether it's in a separate file, and if not extract it to a temporary file, but that's pretty hacky IMO.
When running code using java -jar app.jar, java uses ONLY the class path defined in the manifest of the JAR file (i.e. Class-Path attribute). If the class is in app.jar, or the class is in the class path set in the Class-Path attribute of the JAR's manifest, you can load that class using the following code snippet, where the className is the fully-qualified class name.
final String classAsPath = className.replace('.', '/') + ".class";
final InputStream input = ClassLoader.getSystemResourceAsStream( path/to/class );
Now if the class is not part of the JAR, and it isn't in the manifest's Class-Path, then the class loader won't find it. Instead, you can use the URLClassLoader, with some care to deal with differences between windows and Unix/Linux/MacOSX.
// the class to load
final String classAsPath = className.replace('.', '/') + ".class";
// the URL to the `app.jar` file (Windows and Unix/Linux/MacOSX below)
final URL url = new URL( "file", null, "///C:/Users/diffusive/app.jar" );
//final URL url = new URL( "file", null, "/Users/diffusive/app.jar" );
// create the class loader with the JAR file
final URLClassLoader urlClassLoader = new URLClassLoader( new URL[] { url } );
// grab the resource, through, this time from the `URLClassLoader` object
// rather than from the `ClassLoader` class
final InputStream input = urlClassLoader.getResourceAsStream( classAsPath );
In both examples you'll need to deal with the exceptions, and the fact that the input stream is null if the resource can't be found. Also, if you need to get the InputStream into a byte[], you can use Apache's commons IOUtils.toByteArray(...). And, if you then want a Class, you can use the class loader's defineClass(...) method, which accepts the byte[].
You can find this code in a ClassLoaderUtils class in the Diffusive source code, which you can find on SourceForge at github.com/robphilipp/diffusive
And a method to create URL for Windows and Unix/Linux/MacOSX from relative and absolute paths in RestfulDiffuserManagerResource.createJarClassPath(...)
Construct a URL, you can then load a resource (even in a jar file) using the openStream method.
Assume standard maven setup.
Say in your resources folder you have a file abc.
In Java, how can I get absolute path to the file please?
The proper way that actually works:
URL resource = YourClass.class.getResource("abc");
Paths.get(resource.toURI()).toFile();
It doesn't matter now where the file in the classpath physically is, it will be found as long as the resource is actually a file and not a JAR entry.
(The seemingly obvious new File(resource.getPath()) doesn't work for all paths! The path is still URL-encoded!)
You can use ClassLoader.getResource method to get the correct resource.
URL res = getClass().getClassLoader().getResource("abc.txt");
File file = Paths.get(res.toURI()).toFile();
String absolutePath = file.getAbsolutePath();
OR
Although this may not work all the time, a simpler solution -
You can create a File object and use getAbsolutePath method:
File file = new File("resources/abc.txt");
String absolutePath = file.getAbsolutePath();
You need to specifie path started from /
URL resource = YourClass.class.getResource("/abc");
Paths.get(resource.toURI()).toFile();
Create the classLoader instance of the class you need, then you can access the files or resources easily.
now you access path using getPath() method of that class.
ClassLoader classLoader = getClass().getClassLoader();
String path = classLoader.getResource("chromedriver.exe").getPath();
System.out.println(path);
There are two problems on our way to the absolute path:
The placement found will be not where the source files lie, but
where the class is saved. And the resource folder almost surely will lie somewhere in
the source folder of the project.
The same functions for retrieving the resource work differently if the class runs in a plugin or in a package directly in the workspace.
The following code will give us all useful paths:
URL localPackage = this.getClass().getResource("");
URL urlLoader = YourClassName.class.getProtectionDomain().getCodeSource().getLocation();
String localDir = localPackage.getPath();
String loaderDir = urlLoader.getPath();
System.out.printf("loaderDir = %s\n localDir = %s\n", loaderDir, localDir);
Here both functions that can be used for localization of the resource folder are researched. As for class, it can be got in either way, statically or dynamically.
If the project is not in the plugin, the code if run in JUnit, will print:
loaderDir = /C:.../ws/source.dir/target/test-classes/
localDir = /C:.../ws/source.dir/target/test-classes/package/
So, to get to src/rest/resources we should go up and down the file tree. Both methods can be used. Notice, we can't use getResource(resourceFolderName), for that folder is not in the target folder. Nobody puts resources in the created folders, I hope.
If the class is in the package that is in the plugin, the output of the same test will be:
loaderDir = /C:.../ws/plugin/bin/
localDir = /C:.../ws/plugin/bin/package/
So, again we should go up and down the folder tree.
The most interesting is the case when the package is launched in the plugin. As JUnit plugin test, for our example. The output is:
loaderDir = /C:.../ws/plugin/
localDir = /package/
Here we can get the absolute path only combining the results of both functions. And it is not enough. Between them we should put the local path of the place where the classes packages are, relatively to the plugin folder. Probably, you will have to insert something as src or src/test/resource here.
You can insert the code into yours and see the paths that you have.
To return a file or filepath
URL resource = YourClass.class.getResource("abc");
File file = Paths.get(resource.toURI()).toFile(); // return a file
String filepath = Paths.get(resource.toURI()).toFile().getAbsolutePath(); // return file path
I want to fetch a text file from the directory where my jar file is placed.
Assuming my desktop application 'foo.jar' file is placed in d:\ in an installation of Windows.
There is also a test.txt file in the same directory which I want to read when 'foo.jar' application is running.
How can fetch that particular path in my 'foo.jar' application?
In short I want to fetch the path of my 'foo.jar' file where it is placed.
Note, that actual code does depend on actual class location within your package, but in general it could look like:
URL root = package.Main.class.getProtectionDomain().getCodeSource().getLocation();
String path = (new File(root.toURI())).getParentFile().getPath();
...
// handle file.txt in path
Most JARs are loaded using a URLClassLoader that remembers the codesource from where the JAR has been loaded. You may use this knowledge to obtain the location of the directory from where the JAR has been loaded by the JVM. You can also use the ProtectionDomain class to get the CodeSource (as shown in the other answer; admittedly, that might be better).
The location returned is often of the file: protocol type, so you'll have to remove this protocol identifier to get the actual location. Following is a short snippet that performs this activity; you might want to build in more error checking and edge case detection, if you need to use it in production:
public String getCodeSourceLocation() {
ClassLoader contextClassLoader = CurrentJARContext.class.getClassLoader();
if(contextClassLoader instanceof URLClassLoader)
{
URLClassLoader classLoader = (URLClassLoader)contextClassLoader;
URL[] urls = classLoader.getURLs();
String externalForm = urls[0].toExternalForm();
externalForm = externalForm.replaceAll("file:\\/", "");
externalForm = externalForm.replaceAll("\\/", "\\" + File.separator);
return externalForm;
}
return null;
}
I found the shortest answer of my own question.
String path = System.getProperty("user.dir");