My code executes as expected in Eclipse, but when I attempted to export it to an executable jar, it stopped loading resources. I looked at a lot of examples here, but couldn't seem to figure out what I had done wrong. I reviewed the jar contents and the resource files are all there. The hierarchy in the jar is:
/src/code
/resources files
Within Eclipse project the hierarchy is
/src/classes
/resources/resource files
At program launch, I have a ResourceLoader class load up all of the resources I will need. Because I have so many resource files I'm pulling from, I used the following method in code:
private static Scanner loadScanner(String fileName, ClassLoader classLoader) throws IOException, NullPointerException {
File file;
Scanner sc;
try { //to load the resource file
file = new File(classLoader.getResource(fileName).getFile());
} catch (NullPointerException npe) {
throw new NullPointerException("File " + fileName + " not found in ResourceLoader.loadScanner()");
}
sc = new Scanner(file);
return sc;
}
I call the method with something like:
String fileName = "example.csv";
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
sc = loadScanner(fileName,classLoader);
The error message I am getting is:
file:\C:\...\myjar.jar!\example.csv (The filename, directory name, or volume label syntax is incorrect)
Exception in thread "main" java.lang.NullPointerException
My assumption is that the jar code is looking for a resources folder, and it's not there. How do I need to modify my code or project/jar specs to get it working the way I expect? Appreciate any help.
Also, for educational purposes, the jar file seems to suppress exceptions...? If it can't find the resource file, then why doesn't my NullPointerException catch trigger?
Related
I just started using Java 16 and can't seem to figure out why I am unable to access resource files. I went through some troubleshooting to narrow down at least where I seem to be having a problem.
I'm using IntelliJ IDEA 2021.2 Build #IU-212.4746.92
I created a new project and I chose JavaFX with OpenJDK 16.
It then creates the project with three main files, an Application class, a Controller class, and a FXML file. Once I create the project, I go into the POM file and I chose version 16 of javaFX-controls and javafx-fxml and I tell it to get the latest version of the other libraries it adds automatically into the POM file.
I also copy two folders from a different project into the resources folder - all copy and pasting is done within IntelliJ.
When I run the application that it put there (called HellpApplication), it works fine. And that application uses class.getResource to grab the fxml file and again ... it works just fine. However, when I try to run this class:
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
public class FileResourcesUtils {
public static void main(String[] args) throws URISyntaxException {
FileResourcesUtils app = new FileResourcesUtils();
String fileName = "StyleSheets/AnchorPane.css";
System.out.println("getResourceAsStream : " + fileName);
InputStream is = app.getFileFromResourceAsStream(fileName);
printInputStream(is);
System.out.println("\ngetResource : " + fileName);
File file = app.getFileFromResource(fileName);
printFile(file);
}
private InputStream getFileFromResourceAsStream(String fileName) {
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(fileName);
if (inputStream == null) {
throw new IllegalArgumentException("file not found! " + fileName);
}
else return inputStream;
}
private File getFileFromResource(String fileName) throws URISyntaxException{
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(fileName);
if (resource == null) {
throw new IllegalArgumentException("file not found! " + fileName);
}
else return new File(resource.toURI());}
private static void printInputStream(InputStream is) {
try (InputStreamReader streamReader =
new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)
)
{
String line;
while ((line = reader.readLine()) != null) System.out.println(line);
}
catch (IOException e) {e.printStackTrace();}
}
private static void printFile(File file) {
List<String> lines;
try {
lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
lines.forEach(System.out::println);
}
catch (IOException e) {e.printStackTrace();}
}
}
It throws this error:
getResourceAsStream : StyleSheets/AnchorPane.css
Exception in thread "main" java.lang.IllegalArgumentException: file not found! StyleSheets/AnchorPane.css
at com.simtechdata.test/com.simtechdata.test.FileResourcesUtils.getFileFromResourceAsStream(FileResourcesUtils.java:27)
at com.simtechdata.test/com.simtechdata.test.FileResourcesUtils.main(FileResourcesUtils.java:16)
Then, THE ONLY THING I HAVE TO DO ... is DELETE module-info.java, then that class runs perfectly! HOWEVER, without module-info.java there, I cannot run any FX code...
Here is module-info.java:
module com.simtechdata.test {
requires javafx.controls;
requires javafx.fxml;
opens com.simtechdata.test to javafx.fxml;
exports com.simtechdata.test;
}
Why did I use this class? Because I'm having the exact same problem trying to access resource files in my JavaFX application and this class was the easiest way for me to demonstrate the problem and show that it is specifically connected to the module-info file. When I run my own JavaFX code, it works just fine, until I try to access a resource file like a style sheet to throw onto a control.
What I would like to know is ... what sort of magic trick do I need to do in order to be able to access resource files from my JavaFX 16 application? What am I missing?
I'vr tried all of these different ways of getting to the resource file, but each one gives the same error:
String CSS_ANCHOR_PANE = this.getClass().getResource("StyleSheets/AnchorPane.css").toExternalForm();
String CSS_ANCHOR_PANE = ClassName.getClass().getResource("StyleSheets/AnchorPane.css").toExternalForm();
ClassLoader resource = ClassLoader.getSystemClassLoader();
String CSS_ANCHOR_PANE = resource.getResource("StyleSheets/AnchorPane.css").toExternalForm();
Class<?> resource = this.getClass();
String CSS_ANCHOR_PANE = resource.getResource("StyleSheets/AnchorPane.css").toExternalForm();
And here is a screenshot of the resource folder tree:
Any ideas?
This question is really answered in numerous other places around the StackOverflow site, but I'll pull together various solutions here.
You basically have three options:
Use the getResource() (or getResourceAsStream(), though the former is slightly preferable) method defined in Class, instead of in ClassLoader with the correct path. This avoids any issues with modularity.
Make the project non-modular (i.e. don't define a module-info.java file) and specify the modules to be added as runtime parameters to the JVM. This allows you to use either ClassLoader's getResource() or Class's getResource() method.
Create a modular project and use ClassLoader's getResouce() method, and open the package containing the resource unconditionally.
Solution 1 is detailed here. Since the style sheet you are loading is in the StyleSheets package1, and the class you are loading it from appears (from the stack trace) to be in an unrelated package com.simtechdata.test, you should provide an absolute path:
String CSS_ANCHOR_PANE = this.getClass().getResource("/StyleSheets/AnchorPane.css").toExternalForm();
For solution 2, use
String CSS_ANCHOR_PANE = this.getClass().getClassLoader().getResource("StyleSheets/AnchorPane.css").toExternalForm();
Remove the module-info.java file, and run the application with the JVM arguments
--module-path="/path/to/javafx/modules" --add-modules="javafx.controls,javafx.fxml"
(Replace /path/to/javafx/modules with the actual file system path to the JavaFX modules. The javafx.fxml module is only required in the add-modules parameter if you are using FXML.)
For solution 3, use
String CSS_ANCHOR_PANE = this.getClass().getClassLoader().getResource("StyleSheets/AnchorPane.css").toExternalForm();
Since your resource is in the StyleSheets package1 you need to open that package unconditionally. Add the line
opens StyleSheets ;
to your module-info.java file. See https://stackoverflow.com/a/48790851/2189127
(1) It's very strongly advised to follow standard naming conventions; I'm not even sure if solution 3 will work with a non-standard package name (package names should be all lower-case).
You write a lot about JavaFX but instead you should concentrate on the real problem. As you have shown yourself via your test program this problem is a module system problem and not a JavaFX problem.
You can get rid of this problem by just not using the module system. Just wrap your main method into something like this:
class MyAppLauncher {public static void main(String[] args) {MyApp.main(args);}}
The assumption is that MyApp is your main class which extends Application. The use MyAppLauncher to start your JavaFX program and remove all module system related options.
When trying to load the file in Eclipse the file loads just fine, however when I package the project into a .JAR file using jar-splice it seems that the application can no longer locate its resource files.
Here's the error thrown when the application is run
And here is the method that loads files:
public static File loadFile(String path) throws FileNotFoundException
{
InputStream stream;
stream = FileUtil.class.getClassLoader().getResourceAsStream(path);
System.out.println("Stream = " + stream); //Debug purposes
File file = new File(FileUtil.class.getClassLoader().getResource(path).getFile());
if (!file.exists())
{
System.err.println("Path: " + FileUtil.class.getClassLoader().getResource(path).getPath()); //Also debug purposes
throw new FileNotFoundException();
}
return file;
}
Using those two System.out.printlns it's clear that the application can't find the file based on that path, but if you look at the picture that path is exactly where the file it's looking for is located. I am so confused as this has never happened before, and the path it's saying it can't find the file at is exactly where it is. Any ideas anyone?
Here is the method that files
Here is the method that neither does nor can load resources as Files. It is impossible. Resources are not files and do not live in the file system. You must redesign it to return either a URL or an InputStream, and as you can get both directly from a Class or ClassLoader you don't actually need the method at all.
I am working on building a web application around a Standalone JAR application. Essentially the JAR application reads an input file, processes it and generates an output file.
The input file is placed like this, right next to the JAR.
The code for the JAR application is shown below.
public class FileReadWriteApp {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
File inFiles = new File("../inputFiles");
if (!inFiles.exists()) {
if (inFiles.mkdirs()) {
System.out.println("Input directory is created!");
} else {
System.out.println("Failed to create input directory!");
}
}
Path inputPath = FileSystems.getDefault().getPath("testInput.txt", new String[0]);
Files.copy(inputPath, new FileOutputStream("../inputFiles/testInput.txt"));
String content = new String(Files.readAllBytes(inputPath));
String parsedInput = content.toUpperCase();
new File("../outputFiles").mkdirs();
Files.write(Paths.get("../outputFiles/testOutput.txt"), parsedInput.getBytes());
} catch (IOException exc) {
System.out.println(exc);
System.exit(1);
}
}
Upon executing the JAR in the Command prompt it successfully creates inputFiles and outputFiles folders at one level higher than the JAR file and puts the corresponding input/output files correctly.
However, when I add the JAR as a dependency in the Spring based web application that I am building and place the input file and JAR file at the same location, it can't access the input file.
In the Spring web application I have placed both the input file and the JAR file as shown below:
I can even see them come up in the Target folder in the lib directory as shown below:
In my Spring Controller method, I am executing the main() method of the JAR file like this:
String[] args = new String[] { "" };
filereadwriteapp.FileReadWriteApp.main(args);
However, in the console I see these messages:
Input directory is created!
java.nio.file.NoSuchFileException: testInput.txt
I am not able to understand why the application can't find the file testInput.txt?
Can somebody kindly guide me regarding fixing this issue?
Is there some specific location where I should place the testInput.txt file so that the "dependency" JAR file can access it?
Kindly note that I am not loading a resource from WEB-INF directory from within the Web Application. In my case, I am having issue with a "dependency" JAR loading a resource from a relative path to itself.
Any help in this regard would be highly appreciated.
FileSystems api returns different path , so you need to use classpath to get the file as shown below
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("testInput.txt").getFile());
I have been working on a project that requires the user to "install" the program upon running it the first time. This installation needs to copy all the resources from my "res" folder to a dedicated directory on the user's hard drive. I have the following chunk of code that was working perfectly fine, but when I export the runnable jar from eclipse, I received a stack trace which indicated that the InputStream was null. The install loop passes the path of each file in the array list to the export function, which is where the issue is (with the InputStream). The paths are being passed correctly in both Eclipse and the runnable jar, so I doubt that is the issue. I have done my research and found other questions like this, but none of the suggested fixes (using a classloader, etc) have worked. I don't understand why the method I have now works in Eclipse but not in the jar?
(There also exists an ArrayList of File called installFiles)
private static String installFilesLocationOnDisk=System.getProperty("user.home")+"/Documents/[project name]/Resources/";
public static boolean tryInstall(){
for(File file:installFiles){
//for each file, make the required directories for its extraction location
new File(file.getParent()).mkdirs();
try {
//export the file from the jar to the system
exportResource("/"+file.getPath().substring(installFilesLocationOnDisk.length()));
} catch (Exception e) {
return false;
}
}
return true;
}
private static void exportResource(String resourceName) throws Exception {
InputStream resourcesInputStream = null;
OutputStream resourcesOutputStream = null;
//the output location for exported files
String outputLocation = new File(installFilesLocationOnDisk).getPath().replace('\\', '/');
try {
//This is where the issue arises when the jar is exported and ran.
resourcesInputStream = InstallFiles.class.getResourceAsStream(resourceName);
if(resourcesInputStream == null){
throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
}
//Write the data from jar's resource to system file
int readBytes;
byte[] buffer = new byte[4096];
resourcesOutputStream = new FileOutputStream(outputLocation + resourceName);
while ((readBytes = resourcesInputStream.read(buffer)) > 0) {
resourcesOutputStream.write(buffer, 0, readBytes);
}
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
} finally {
//Close streams
resourcesInputStream.close();
resourcesOutputStream.close();
}
}
Stack Trace:
java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.
All help is appreciated! Thanks
Stack Trace:
java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.
The name if the resource is wrong. As the Javadoc of ClassLoader.getResource(String) describes (and Class.getResourceAsStream(String) refers to ClassLoader for details):
The name of a resource is a /-separated path name that identifies
the resource.
No matter whether you get your resources from the File system or from a Jar File, you should always use / as the separator.
Using \ may sometimes work, and sometimes not: there's no guarantee. But it's always an error.
In your case, the solution is a change in the way that you invoke exportResource:
String path = file.getPath().substring(installFilesLocationOnDisk.length());
exportResource("/" + path.replace(File.pathSeparatorChar, '/'));
Rename your JAR file to ZIP, uncompress it and check where did resources go.
There is a possibility you're using Maven with Eclipse, and this means exporting Runnable JAR using Eclipse's functionality won't place resources in JAR properly (they'll end up under folder resources inside the JAR if you're using default Maven folder names conventions).
If that is the case, you should use Maven's Assembly Plugin (or a Shade plugin for "uber-JAR") to create your runnable JAR.
Even if you're not using Maven, you should check if the resources are placed correctly in the resulting JAR.
P.S. Also don't do this:
.getPath().replace('\\', '/');
And never rely on particular separator character - use java.io.File.separator to determine system's file separator character.
Hi i am using maven web project and want to write something to file abc.properties. This file in placed in standard /src/main/resource folder. My code is:
FileWriter file = new FileWriter("./src/main/resources/abc.properties");
try {
file.write("hi i am good");
} catch (IOException e) {
e.printStackTrace();
} finally {
file.flush();
file.close();
}
But it does not work as path is not correct. I tried many other examples but was unable to give path of this file.
Can you kindly help me in setting path of file which is placed in resources folder.
Thanks
I think you're confusing buildtime and runtime. During buildtime you have your src/main/java, src/main/resources and src/main/webapp, but during runtime these are all bundled in a war-file. This means there's no such thing as src/main/resources anymore.
The easiest way is to write to a [tempFile][1] and write to that file. The best way is to configure your outputFile, for instance in the wqeb.xml.
[1]: http://docs.oracle.com/javase/6/docs/api/java/io/File.html#createTempFile(java.lang.String, java.lang.String)
If your file is dropped under src/main/resources, it will end up under your-webapp/WEB-INF/classes directory if you project is package as a Web application i.e. with maven-war-plugin.
At runtime, if you want to files that are located under the latter directory, which are considered as web application resources, thus are already present in the application classpath, you can use the getResourceAsStream() method either on the ServletContext or using the current class ClassLoader:
From current thread context:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream is = classLoader.getResourceAsStream("abc.properties");
FileWriter file = new FileWriter(new File(new FileInputStream(is)));
// some funny stuff goes here
If you have access to the Servlet context:
ServletContext context = getServletContext();
InputStream is = context.getResourceAsStream("/abc.properties");
FileWriter file = new FileWriter(new File(new FileInputStream(is)));
// some funny stuff goes here
Notice the leading slash in the latter example.