IllegalStateException: Location is not set when running jar file - java

I wrote a program which works fine when running it in IntelliJ.
I created artifact, so I can also run it from executable jar, but it doesn't work.
GUI of my app simply does not appear. I run the jar from command line and I got IllegalStateException: Location is not set which is probably caused by this class:
public class SingleIcon extends AnchorPane {
public SingleIcon() {
System.out.println(getClass().getResource("../resources/fxml/SingleIcon.fxml"));
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("../resources/fxml/SingleIcon.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
#FXML private void initialize() {}
}
SingleIcon is created multiple times during initialization of the program.
The System.out.println() line gives me this line numerous times:
file:/C:/Users/Patryk/IdeaProjects/Battleships/out/production/Battleships/battleships/resources/fxml/SingleIcon.fxml
Running jar (java -jar app.jar) from command line gives me null and IllegalStateException.
I checked path I pass to getResource() method and it is correct - I have to get back by one directory and then go through to SingleIcon.fxml. Anyway, it wouldn't work in IntelliJ if the path was not correct.
Do you have any ideas?

You are using a relative path in your call to getResource. This is returning null which is causing the exception. If the resources are packaged with the jar simply call it like this
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/fxml/SingleIcon.fxml"));
Notice the preceding forward slash.

Related

InputStream gives IllegalArgumentException for just the jar file [duplicate]

The line persistenceProperties.load(is); is throwing a nullpointerexception in the following method. How can I resolve this error?
public void setUpPersistence(){
final Properties persistenceProperties = new Properties();
InputStream is = null;
try {
is = getClass().getClassLoader().getResourceAsStream("src/test/samples/persistence.properties");
persistenceProperties.load(is);
}catch (IOException ignored) {}
finally {
if (is != null) {try {is.close();} catch (IOException ignored) {}}
}
entityManagerFactory = Persistence.createEntityManagerFactory(
"persistence.xml", persistenceProperties);
}
I have tried to experiment with this by moving the class that contains the method to various other locations within the application structure, and also by changing the line of code preceding the error in the following ways:
is = getClass().getClassLoader().getResourceAsStream("persistence.properties");
is = getClass().getClassLoader().getResourceAsStream("/persistence.properties");
is = getClass().getClassLoader().getResourceAsStream("/src/test/samples/persistence.properties");
is = getClass().getClassLoader().getResourceAsStream("other/paths/after/moving/persistence.properties");
But the error is still thrown every time the method is called.
Here is a printscreen of the directory structure of the eclipse project. The class containing the method is called TestFunctions.java, and the location of persistence.properties is shown:
**EDIT: **
As per feedback below, I changed the method to:
public void setUpPersistence(){
final Properties persistenceProperties = new Properties();
InputStream is = null;
try {
is = getClass().getClassLoader().getResourceAsStream("persistence.properties");
persistenceProperties.load(is);
}catch (IOException i) {i.printStackTrace();}
finally {
if (is != null) {try {is.close();} catch (IOException ignored) {}}
}
entityManagerFactory = Persistence.createEntityManagerFactory(
"persistence.xml", persistenceProperties);
}
I also moved mainTest.TestFunctions.java to src/test/java. Together, these all cause the following new stack trace:
Exception in thread "main" java.lang.NoClassDefFoundError: maintest/TestFunctions
at maintest.Main.main(Main.java:7)
Caused by: java.lang.ClassNotFoundException: maintest.TestFunctions
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
... 1 more
Short answer:
Move persistence.properties to src/main/resources, have both Main.java and TestFunctions.java in src/main/java, and use
getClass().getClassLoader().getResourceAsStream("persistence.properties");
to load the properties file.
Long answer with an explanation:
As others have hinted at - in a Maven project structure, you (typically) have two directory trees: /src/main and /src/test. The general intent is that any "real" code, resources, etc should go in /src/main, and items that are test-only should go in /src/test. When compiled and run, items in the test tree generally have access to items in the main tree, since they're intended to test the stuff in main; items in the main tree, however, do not typically have access to items in the test tree, since it's generally a bad idea to have your "production" code depending on test stuff. So, since Main.java depends on TestFunctions.java, and TestFunctions.java depends on persistence.properties, if Main is in src/main then both TestFunctions and persistence.properties need to be as well.
Two things:
First, try a path of test/samples/... or /test/samples/...
Secondly, and much more importantly, don't ever, ever, ever write this:
try {
// some stuff
} catch (IOException ignored) {}
All this says is: do some stuff, and if it goes wrong, then fail silently. That is never the right thing to do: if there's a problem, you want to know about it, rather than madly rushing on as if nothing had happened. Either do some sensible processing in your catch block, or else don't have the try/catch and add a throws IOException to your method signature so it can propagate upwards.
But at the moment, you're just sweeping things under the carpet.
ClassLoader.getResourceAsStream() loads resources as it does for loading classes. It thus loads them from the runtime classpath. Not from the source directories in your project.
Your class Main is in the package maintest, and its name is thus maintest.Main. I know that without even seeing the code because Main.java is under a directory named maintest, which is at directly under a source directory.
The persistence.properties file is directly under a source directory (src/test/resources). At runtime, it's thus at the root of the classpath, in the default package. Its name is thus persistence.properties, and not src/test/samples/peristence.properties. So the code should be
getClass().getClassLoader().getResourceAsStream("persistence.properties");
Nothing will ever be loadable from the samples directory, since thisdirectory is not under any source directory, and is thus not compiled by Eclipse, and is thus not available to the ClassLoader.
I will try to make it more Simple for this Question!
Here your main class is in src/main/java as you mentioned, so you should create another source for storing your properties file saying src/main/resources which you had already done and just store your properties file in this source, so in Run time it will directly refer this path and access the file,
You can add this piece of code as well to access the properties file
is = ClassLoader.getSystemResourceAsStream("your_properties_file");
and you can use load(is) accordingly.
To Conclude
If your main class is in src/main/java then you should keep your properties file in src/main/resources and use the respective snippet to load this.
OR
If your main class is in src/test/java then you should keep your properties file in src/test/resources and use the respective snippet to load this.
Your IDE works with two different scopes:
production scope: src/main/java + src/main/resources folders and
test scope: src/test/java + src/test/resources
Seems you are trying to execute you program from production scope, while persistence.properties file is placed into test scope.
How to fix:
Place your test into src/test/java or
Move persistence.properties into src/main/resources
InputStream is = this.getClass().getResourceAsStream("/package_name/property file name")
PropertyFileOject.load(is)
In my case error was due to maven was not treating my config folder inside src/main/java as source folder.
Recreated config package in src/main/java ..Copied files to it and re-compiled using maven. Files were there in target directory and hence in war. Error resolved.
I recently had the same problem and came upon the solution that I had to put my resources in a path the same way organized as where was getClass().getResourceAsStream(name) situated. And I still had a problem after doing that. Later on, I discovered that creating a package org.smth.smth only had created a folder named like that "org.smth.smth" and not a folder org with a folder smth and a folder inside smth... So creating the same path structure solved my problem. Hope this explanation is understandable enough.

Unknown Path FXML document

We're writing an Java application using JavaFX. At this time we have 3 different forms:
Login
Game window
Registration
For our next iteration, we want to implement the Registration form, but we get the IOException error Unknown Path
It's about this piece of code:
FXMLLoader registrationLoader = new FXMLLoader();
try{
mainroot = (Parent)registrationLoader.load(this.getClass().getResource("FXMLRegistration.fxml").openStream());
Stage registrationStage = new Stage();
Scene scene = new Scene(mainroot);
registrationStage.setScene(scene);
registrationStage.setTitle("Register your account");
registrationStage.show();
} catch(IOException ex)
{
System.out.print(ex.getMessage());
}
The above code is working when I change FXMLRegistration.fxml to FXMLDocument.fxml or FXMLLoader.fxml.
When I change
mainroot = (Parent)registrationLoader.load(this.getClass().getResource("FXMLRegistration.fxml").openStream());
to
mainroot = (Parent)registrationLoader.load(Paths.get("src/hackattackfx/FXMLRegistration.fxml").toUri().toURL());
source
I get the absolute path in the debugger output, which is correct when I use it with the file command in terminal.
I hope someone could help us with this error.
Thanks in advance!
EDIT
I Changed some code to the following:
FXMLLoader registrationLoader = new FXMLLoader(getClass().getResource("/FXMLRegistration.fxml"));
mainroot = (Parent)registrationLoader.load();
but this will return an IllegalStateException: Location is not set.
When I remove / before /FXMLRegistration.fxml, I get to my catch block printing the full path of the file:
file:/Users/juleskreutzer/Documents/github/PTS3/HackAttackFX/dist/run1793658053/HackAttackFX.jar!/hackattackfx/FXMLRegistration.fxml
Also changing the path to src/hackattackfx/FXMLRegistration.fxml will give the IllegalStateException: Location not set.
Project Structure
We use different packages in our application. all these packages are within the default package: hackattackfx
The packages in the default package are:
Default Package
Exceptions
Interfaces
enums
Resources
Templates
JSON Package
My FXML documents are located in the default package (hackattackfx). If it's not 100% clear how I arranged my files, please take a look at my Github repo
So, I got all curious to find out the root cause, I cloned the repo and found that the actual issue was the following error and the one that was posted by the OP in the question
Caused by: java.lang.NullPointerException
at hackattackfx.FXMLRegistrationController.initialize(FXMLRegistrationController.java:67)
Which means that in the controller pane was null.
This was because the fxml was missing a declaration of fx:id .
Add fx:id="pane" to the AnchorPane declaration of FXMLRegistration.fxml and things should just work fine.
You need to start your Path with /
this is working for me:
final String fxmlPath = "/fxml/Main.fxml";
final FXMLLoader loader = new FXMLLoader(this.getClass().getResource(fxmlPath));
Main.fxml is located in the resource folder (for me: /src/main/resources/fxml/)

Update java desktop app

I am trying to develop a module that can update my running Java Desktop App.
The problem is that I have to replace the actual running jar with another jar, all the while displaying an image and a progress bar with the remaining time of the update process.
One solution I thought about is that I can put a jar in my main jar, and when launching the update process, to extract that second jar which will display the image and the progess bar, and also which will replace the old main jar with a new main jar.
My question is if this is possible and how can I do it.
I do not have a lot of experience with java and java packaging so if you have any examples or links, it would be of great help for me.
Thank you very much.
R.
Run this code when press UPDATE button ..
if(Desktop.isDesktopSupported()){
try {
Desktop.getDesktop().open(new File("update.jar"));
System.exit(0);
} catch (IOException ex) {
}
}
This will open update.jar and close main.jar. Now run this code from main class of update.jar
//wait sometime for terminate main.jar
try{
Thread.sleep(5000);
} catch(Exception e){
e.printStackTrace();
}
if(isUpdateVersionAvailable()) { //first check update from database
if(copyMainJarFileFromServer()){ //copy newMain.jar from server and paste
new File("main.jar").delete(); //delete main.jar
rename(new File("newMain.jar")); //rename newMain.jar to main.jar
}
}
boolean isUpdateVersionAvailable() {
//todo
}
boolean copyMainJarFileFromServer() {
//todo
}
void rename(File file){
file.renameTo(new File("main.jar"));
}
You can have a starter jar that checks for updates and launches the app from the main jar.
It will show start logo, an image, that standard java can display at start-up.
The start0er could also be used to restart the app in another interface language.
package starter;
...
public class StarterApp {
public static void main(String[] args) {
String workDir = System.getProperty("user.dir");
Path mainJar = Paths.get(workDir + "...");
Path nextMainJar = Paths.get(workDir + "...");
if (Files.exists(nextMainJar)) {
Files.copy(nextMainJar, mainJar, StandardCopyAction.REPLACE_EXISTING);
}
URLClassLoader classLoader = new URLClassLoader(new URL[] {mainJar.toURL()});
Class<?> appClass = classLoader.find("mainjar.MainApp");
... instantiate the app
}
As you see the main jar must not be loaded from too early, maybe not be on the class path entirely, and hence the use of a separate ClassLoader. The same might probably be done with the main jar on the class path of the starter app, and using Class.forName("mainjar.MainApp"). The Class-Path can be specified in META-INF/MANIFEST.MF.
The secundary jars may reside in a lib/ directory.
For those readers wanting more modular, service oriented, updateable apps, one could make an OSGi application, a container for bundles (=jars), that provide exchangable services and life-time control.

Load jar dynamically at runtime?

My current java project is using methods and variables from another project (same package). Right now the other project's jar has to be in the classpath to work correctly. My problem here is that the name of the jar can and will change because of increasing versions, and because you cannot use wildcards in the manifest classpath, it's impossible to add it to the classpath. So currently the only option of starting my application is using the -cp argument from the command line, manually adding the other jar my project depends on.
To improve this, I wanted to load the jar dynamically and read about using the ClassLoader. I read a lot of examples for it, however I still don't understand how to use it in my case.
What I want is it to load a jar file, lets say, myDependency-2.4.1-SNAPSHOT.jar, but it should be able to just search for a jar file starting with myDependency- because as I already said the version number can change at anytime. Then I should just be able to use it's methods and variables in my Code just like I do now (like ClassInMyDependency.exampleMethod()).
Can anyone help me with this, as I've been searching the web for a few hours now and still don't get how to use the ClassLoader to do what I just explained.
Many thanks in advance
(Applies to Java version 8 and earlier).
Indeed this is occasionally necessary. This is how I do this in production. It uses reflection to circumvent the encapsulation of addURL in the system class loader.
/*
* Adds the supplied Java Archive library to java.class.path. This is benign
* if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws MyException
{
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final java.lang.NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new MyException(e);
}
}
I needed to load a jar file at runtime for both java 8 and java 9+. Here is the method to do it (using Spring Boot 1.5.2 if it may relate).
public static synchronized void loadLibrary(java.io.File jar) {
try {
java.net.URL url = jar.toURI().toURL();
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{url});
} catch (Exception ex) {
throw new RuntimeException("Cannot load library from jar file '" + jar.getAbsolutePath() + "'. Reason: " + ex.getMessage());
}
}

How to launch a resource jar from inside another java app - with Solution

EDITED with solution (below...)
I have a Splash screen that is packaged into it's own jar. It works.
I can call the Splash.jar from inside another java app by:
Desktop.getDesktop().open(new File("/Applications/Eclipse/Splash.jar"));
and it works just fine. But, that's pretty limited. So, I created a res folder in the new app and dragged the Splash.jar into it.
Now, how do I call it/run it from the main class of the new app??
I looked at all the related posts and see no clear approach...
Thanks
SOLUTION:
I found the solution - so simple. First, the credit to avjava.com for their clear and excellent tutorial on doing this ( Run Another Jar from a Jar ). So, now I can run the Splash (or other .jar) just as hoped for.
The answer is here - posted in the form of complete code:
package test;
import java.io.IOException;
public class RuntimeExecTest1 {
public static void main(String[] args) {
try {
System.out.println("TextEdit I hope");
Runtime runTime = Runtime.getRuntime();
Process process = runTime.exec(
"java -jar /your directory/your app.jar");
try {
Thread.sleep(5000); // keep in open 5000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Closing TextEdit, I hope");
process.destroy(); // kill the process of running the .jar
} catch (IOException e) {
e.printStackTrace();
}
}
}
We don't know how your existing Splash Screen works...
Java AWT support for Splash Screen:
If you are using the Java built-in support for splash screens (as specified in SplashScreen Javadoc) then you need to use a command line switch, or better yet, modify your MANIFEST file in order to reference your Splash Screen:
Manifest-Version: 1.0
Main-Class: Test
SplashScreen-Image: filename.gif
I don't know if, for this particular case, you can reference files in a different JAR. In the worst case, you can unpack the existing JAR (they are just ZIP files) and get the image file in order to include it in your own main jar.
Possibly custom Splash:
If your Splash is created using custom code, then you need the documentation about how to load it. At least, you'd need to add Splash.jar to the classpath of your application and, from your app, call the necessary method or load the appropriate resource.
All the resources and classes contained in .jar files that are added to the classpath are available from your app.
You could create a new URLClassLoader with the Splash.jar and then use reflections to execute the main method.
URL[] urls = new URL[] { new URL("res/Splash.jar") };
ClassLoader cl = new URLClassLoader(urls);
Class<?> clazz = cl.loadClass("splash.Main");
Method method = clazz.getMethod("main", String[].class);
method.invoke(null, new String[0]);
add the resource path to your CLASSPATH envoirment variable and you can use it without modifying your existing code
if your running linux
export CLASSPATH=yourpath;
and if your running windows:
from My Computer right click > properties
OR
if you dont want to add it to CLASSPATH ENV variable,
then
java -classpath="your path to jar" yourclass
Why not define Splash.jar as an external jar and go about using all its routines. Much like JDBC.

Categories