Cannot delete external JAR file after loading in Image [duplicate] - java

This question already has answers here:
file.delete() returns false even though file.exists(), file.canRead(), file.canWrite(), file.canExecute() all return true
(17 answers)
Closed 2 years ago.
I am currently busy with building a simple javafx application. In this application, you can add and remove .jar files from within the application, from which I am gathering some information. One of the things I use from these .jar files are textures stored as images.
Whenever I load an Image using a path from the jar file, I am unable to remove this .jar file later on. This is quite logical as the .jar file is now being used by this Image, but for some reason, I am unable to delete the .jar file from within the application even when removing all references to this Image and calling the garbage collector.
The way I am currently trying to delete the file is the following:
File file = new File("mods/file.jar");
file.delete();
At some point in the application I initialize the image as follows:
Image block = new Image(pathToJar);
I have tried the following things to resolve my problem:
Set block = null; and call System.gc();
Call Image.cancel() and call System.gc();
I have also tried the above in combination with System.runFinalization();
I tried removing the .jar file when the stage is closing in hopes of everything being unloaded, but without success.
At this point in time, I am not quite sure why I am unable to actually delete the .jar file. Could anyone possibly explain to me why this is the case and how it could be resolved? Many thanks in advance!
EDIT: the purpose of the application was not clear enough. I am making a color picker that takes average colors of textures from .jar files (Minecraft mods). These .jar files are to be added and deleted from the application in a flexible way. Once you do not want to consider textures of a certain .jar file anymore, you just remove it from the application and be done with it. For ease of use, I made copies of the .jar files so I can access them relatively. Once you are done with the .jar file I want to remove this copy as it will just take in unnecessary storage.
I have narrowed the problem (thus far) down to the initialization of an Image. On request, here a MRE:
public class example extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// Read image, needed at some point in my code
Image block = new Image("jar:File:./mods/botania.jar!/assets/botania/textures/blocks/alfheim_portal.png");
// Make it so that jar is not used anymore
block.cancel();
block = null;
System.gc();
// Try to delete the file after being done with it
File delete = new File("mods/botania.jar");
Files.delete(delete.toPath());
}
}
After initializing the image, and thereafter removing references to it, it should be cleaned by the garbage collector (at least to my knowledge). Instead, now it just gives the following error:
Caused by: java.nio.file.FileSystemException: mods\botania.jar: The
process cannot access the file because it is being used by another
process.

I can't reproduce the issue on my system (Java 14 on Mac OS X); however running it on Windows 10 with the same JDK/JavaFX versions produces the exception you describe.
The issue appears to be (see https://stackoverflow.com/a/54777849/2067492, and hat-tip to #matt for digging that out) that by default the URL handler for a jar: URL caches access to the underlying JarFile object (and doesn't call close(), even if an InputStream obtained from it is closed). Since Windows holds on to file handles in those situations, the underlying OS is unable to delete the jar file.
I think the most natural way to address this is to use the java.util.jar API (or just the plain java.util.zip API), which will give you far more control over closing resources than generating a URL and passing the URL to the Image constructor.
Here's a self-contained example using this approach, which works both on my Mac and on my Windows 10 virtual VM:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import javax.imageio.ImageIO;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void init() throws Exception {
// create an image in a jar file:
BufferedImage image = new BufferedImage(100, 100, ColorSpace.TYPE_RGB);
Graphics graphics = image.getGraphics();
graphics.setColor(new Color(0xd5, 0x5e, 0x00));
graphics.fillRect(0, 0, 100, 100);
graphics.setColor(new Color(0x35, 0x9b, 0x73));
graphics.fillOval(25, 25, 50, 50);
JarOutputStream out = new JarOutputStream(new FileOutputStream("test.jar"));
ZipEntry entry = new ZipEntry("test.png");
out.putNextEntry(entry);
ImageIO.write(image, "png", out);
out.close();
}
#Override
public void start(Stage stage) throws Exception {
assert Files.exists(Paths.get("test.jar")) : "Jar file not created";
JarFile jarFile = new JarFile("test.jar");
JarEntry imgEntry = jarFile.getJarEntry("test.png");
InputStream inputStream = jarFile.getInputStream(imgEntry);
Image img = new Image(inputStream);
jarFile.close();
// The following line (instead of the previous five lines) works on Mac OS X,
// but not on Windows
//
// Image img = new Image("jar:file:test.jar!/test.png");
Files.delete(Paths.get("test.jar"));
assert ! Files.exists(Paths.get("test.jar")) : "Jar file not deleted";
ImageView iview = new ImageView(img);
Scene scene = new Scene(new BorderPane(iview), 200, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}

Related

JavaFX not accepting the correct URL for Image object [duplicate]

This question already has an answer here:
How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?
(1 answer)
Closed last year.
While attempting to mess around with JavaFX and its basic functions on java 1.8 I kept running into the issue that my Image objects would not accept the URL for a png file contained within my src folder. The icon image object gave me trouble at first but when I copied the file name via the "Copy Path/Reference..." option it worked. This was not the case for the coinImage object where no matter how I format the image URL it will not accept it. To clarify, my files are named "icon.png" and "coin.png" and they are placed in the src folder. I have tried the following formats:
Image coinImage = new Image("coin.png");
Image coinImage = new Image("/coin.png");
Image coinImage = new Image("file:coin.png");
Image coinImage = new Image("file:/coin.png");
Below is the code which most relates to the problem to reduce on the size of this question:
public class MainStage extends Application {
public void start(Stage stage) throws Exception{
Group root = new Group();
Scene scene = new Scene(root,1000,1000,Color.LIGHTSKYBLUE);
Image icon = new Image("icon.png");
Image coinImage = new Image("coin.png");
ImageView imageView = new ImageView(coinImage);
imageView.setX(400);
imageView.setY(500);
root.getChildren().add(imageView);
stage.getIcons().add(icon);
stage.setTitle("Test");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here are all of my imports as well but I do not think they are the issue here:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
If any additional information is needed I should be able to provide it. Thanks.
It is recommended to keep images and resources in a dedicated assets folder.
You need to have the assets folder in your classpath.
Set the assets directory as a resource directory and then create a javafx Image from location: "/coinImage.png":
If you are using IntelliJ To set folder as resource directory follow this guide
This issue is coming from the fact that Java will search for your image file in the bin directory. You should put your image in the bin directory.
I added a small print in your code that can help for debugging
System.out.println("GET PATH"+this.getClass().getClassLoader().getResource("").getPath());
The output that i got
GET PATH /D:/Nabil-local/eclipse-workspace/Stackoverflow/bin/
I think there is another way to specify a folder

JCEF (Java Chromium Embedded Framework) Browser does not load content; only blank screen on Eclipse

I have been trying to get the Java version of Chromium Embedded Framework (JCEF) to work on Eclipse for some time. I am able to verify that the library files are working correctly, since if I run the included sample class files on the VM, the program runs and some webpage is displayed. However, if I run the program from Eclipse, the program will always display a blank window. I am able to verify that the library binary jcef_helper.exe is successfully run, but not matter how I link the .jar files and other library files, the webpage will not generate and there will always be a blank screen. I cannot pinpoint the issue here. I tried specifying path, adding the JCEF library path of my OS environment variables PATH field to no avail. I have followed the documentation, even sample files behave the same way when I have anything to do with compilation/ run. One note of interest, my Eclipse console will display this during run:
initialize on Thread[AWT-EventQueue-0,6,main]
Perhaps there is a thread issue?
Code in question:
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import org.cef.CefApp;
import org.cef.CefApp.CefAppState;
import org.cef.CefClient;
import org.cef.CefSettings;
import org.cef.browser.CefBrowser;
import org.cef.handler.CefAppHandlerAdapter;
public class WebV
{
public static void main(String args[])
{
Dimension dispDimension = Toolkit.getDefaultToolkit().getScreenSize();
int width = (int)Math.round(dispDimension.getWidth() / 2);
int height = (int)Math.round(dispDimension.getHeight() / 2);
CefApp.addAppHandler(new CefAppHandlerAdapter(null)
{
#Override
public void stateHasChanged(CefAppState state)
{
if(state == CefAppState.TERMINATED)
{
System.exit(0);
}
}
});
CefSettings settings = new CefSettings();
settings.windowless_rendering_enabled = false;
CefApp cefApp = CefApp.getInstance(settings);
CefClient client = cefApp.createClient();
CefBrowser browser = client.createBrowser("http://www.google.com", false, false);
Component browserComponent = browser.getUIComponent();
JFrame frame = new JFrame();
frame.setTitle("WebV");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(width, height);
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.add(browserComponent);
frame.setVisible(true);
}
}
Tested with both Java 17 and Java 8.
Run on Eclipse, with VM args:
-Djava.library.path=./bin/lib/win64
Any inupt would be greatly appreicated.
This answer was kindly provided by 'Yanovsky' at the JCEF forums.
The simple example provided by the JCEF package is incomplete.
We need to add a CefMessageRouter instance to our browser client. This is demonstrated in the detailed examples, but not the simple one. This code should be added before we create our CefBrowser instance and after we have created a CefClient object:
CefMessageRouter msgRouter = CefMessageRouter.create();
msgRouter.addHandler(new MessageRouterHandler(), true);
msgRouter.addHandler(new MessageRouterHandlerEx(client), false);
client.addMessageRouter(msgRouter);
For reference, this is my post in the JCEF forum:
https://magpcss.org/ceforum/viewtopic.php?f=17&t=18834&sid=45381db639d1621f2f8b38b4d8848800
I just copied and pasted your code in eclipse and added the jars and ran the code and voila, google.com opened without any issues.
I didnt have to add any more new lines as suggested by mindoverflow.
The webpage was visible in the window after a few secs and not immediately.
Here's what popped up after running the code.
And in the eclipse console, I see this
initialize on Thread[AWT-EventQueue-0,6,main] with library path C:\path\to\dlls

Display an image in java

public void loadStdImage() throws IOException
{
Image image = ImageIO.read(this.getClass().getResource("/Resources/Images/Student/Capture.png")); //Line 350
ImageIcon icon = new ImageIcon(image);
JLabel lblImage = new JLabel(icon);
lblImage.setIcon(icon);
lblImage.setBounds(753, 50, 149, 171);
add(lblImage);
}
I tried many things... but nothing works out. Continuously showing the following run-time error
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: input == null!
at javax.imageio.ImageIO.read(Unknown Source)
at View.Student.loadStdImage(Student.java:350)
Project folder structure is:
edit:
Found the solution. See the change of icon of the resource folder in the following picture and the above image. I added my resource folder to Java Build Path. Right click on your project, go to properties, then select 'Java Build Path', from there add your folder to java build path.
Cheers
enter image description here
welcome to SO. As you are new here, please read this - https://stackoverflow.com/help/mcve
Let me help you with this for now.
I have standard Eclipse project:
and my test class looks like (minimal):
package q34460547;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
public class LoadTest {
public static void main(String[] args) throws IOException {
new LoadTest().loadStdImage();
}
public void loadStdImage() throws IOException {
Image image = ImageIO.read(this.getClass().getResource("/ScreenShot005.png"));
}
}
and now, when I used
ImageIO.read(this.getClass().getResource("/ScreenShot005.png"));
image is loaded from res so called source folder in Eclipse.
When I used
ImageIO.read(this.getClass().getResource("ScreenShot005.png"));
image s loaded from the folder in which LoadTest.java file is (to be precise it is also compiled to same folder - in Eclipse it's bin).
You can find more info for example here - What is the difference between Class.getResource() and ClassLoader.getResource()?
edit:
The image has to be on classpath (when using Class.getResource), that's why it was not loaded from Resources folder. There are two options, use another version of ImageIO.read() or make your Resources folder a source folder:

Java Swing: Runnable JAR file does not work when including images

The code below works fine in Eclipse (both image-handling possibilities). But when exporting as a Runnable JAR File, and double-clicking the .JAR, nothing happens. If I comment out the image parts of the code, the .JAR runs fine as an export and the frame builds. So it seems the getting of the image is causing the .JAR to fail.
I've got the strawberry.jpg file sitting in 'C:\Users\sean\workspace\myApps\bin\testing' Could you advise if the issue is with my code?
(Code first modified here: Java Swing: unable to load image using getResource)
package testing;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
public class IconTest {
public static void main(String[] arguments) throws IOException {
JFrame frame1 = new JFrame();
frame1.setTitle("Frame1");
frame1.setSize(500, 500);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout flo = new FlowLayout();
frame1.setLayout(flo);
//POSSIBILITY 1 TO HANDLE IMAGE
InputStream resourceAsStream = IconTest.class.getResourceAsStream("strawberry.jpg");
Image image = ImageIO.read(resourceAsStream);
JLabel label1 = new JLabel(new ImageIcon(image));
//POSSIBILITY 2 TO HANDLE IMAGE
/* java.net.URL url= IconTest.class.getResource("strawberry.jpg");
BufferedImage watermarkImage = ImageIO.read(url);
JLabel label1 = new JLabel(new ImageIcon(watermarkImage));*/
frame1.add(label1);
frame1.setVisible(true);
}
}
Put the image where you have the source file because it points to the source directory
InputStream resourceAsStream = IconTest.class.getResourceAsStream("strawberry.jpg");
and then generate runnable jar file and then right click the jar and give run as java platform binary
POSSIBILITY 3 TO HANDLE IMAGE :
You can check that resource that you are fetching is exist or not. It resource not found then you can create JLabel with alternative text also...
JLabel label1 ;
URL imageUrl = IconTest.class.getClassLoader().getResource("strawberry.jpg");
if ( imageUrl != null ) {
label1 = new JLabel(new ImageIcon(imageUrl));
} else {
label1 = new JLabel("Alternative text");
}
Following Maven standard directory layout might be helpful:
InputStream resourceAsStream = IconTest.class.getResourceAsStream("/strawberry.jpg")
It should start from "/". Also check whether the jar actually have the image. Just unpack it and check whether the image is actually added.
The guidance offered here in troubleshooting the problem helped greatly - thank you to everyone. I tested contents of an exported JAR; I used diagnostic code suggested by Kishan to determine if my code could "see" the image. I believe it might have something to do with the way Eclipse works/refreshes if I move images around in the file system instead of the Eclipse import function.
Finally, in order to get it to work, I made the following changes:
I created a new project and two sub-packages - one for 'resources' and one for my code class. Then I right-clicked > import on the resources package in Eclipse to get the image.
The only thing to change in my code is ....getResourceAsStream("/resources/strawberry.jpg");

Accessing Data inside of a .jar

I am currently putting a program into a .jar, and have difficulties telling it where to get its data from. The data was inside of a file in the project, and I am sure that it is located in the jar as well. But I have no clue on how to get a path into a jar.
I found the getClass().getClassLoader().getResourceAsStream() method online to get an input stream into the jar, but since I used FileReaders all the time, I dont know what to do with it as well..
I`d be very thankful for any help.
Edit:
Here is a picture of how the directory is organized:
My command window shows what happens if I run the .jar. Nullpointer in line 30. I tried it with and without .getClassLoader(), it just wont find it.
Here is the inside of the jar:
again, app is where the class files are in. Hence, via class.getResource.. I should be able to search in DataPackeg. Man, this is wearing me out.
A key concept to understand is that files don't exist inside of jars. You must instead get your data as a read-only resource, and you will need to use a path that is relative to path of your class files.
If you're still stuck, you may need to tell us more specifics about your current program, its structure, what type of data you're trying to get, where it's located in the jar file, and how you're trying to use it.
For instance, say your package structure looked like this:
So the class file is located in the codePackage package (this is Eclipse so the class files live in a universe parallel to the java files), and the resource's location is in the codePackage.images package, but relative to the class file it is the images directory, you could use the resource like so:
package codePackage;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class ClassUsesResources {
private JLabel label = new JLabel();
public ClassUsesResources() {
try {
BufferedImage img = ImageIO.read(getClass().getResourceAsStream(
"images/img001s.jpg"));
ImageIcon icon = new ImageIcon(img);
label.setIcon(icon);
JOptionPane.showMessageDialog(null, label);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
public static void main(String[] args) {
new ClassUsesResources();
}
}

Categories

Resources