right way to make a jar file from javafx application - java

I have a javafx project, which contains multiple paths for images and text files :
private Image imgMan = new Image(getClass().getResource("../man.gif").toExternalForm());
FileHelper.resetScores("./bin/application/MAP/BestScores.txt");
...
When i launch from eclipse, it work normally, and access to images and files without any problem.
But when i try to export my project to a jar file, it export correctly, but it don't launch !
I try to launch it from cmd, the trace of stack said that he don't know the paths...
Caused by: java.lang.NullPointerException
at application.Client.(Client.java:31)
(line 31 in my code refer to the first line of code given in the question)
I try to create a resource folder and put all files into it, but no result.
So what is the best way to make it ?
where must i create the resource folder ?
and how to access the files into it from the code ?
Thank you

There are several thing you should check:
verify the path in your jar against the class you are looking. Your image must be there.
verify you have successfully loaded a resource because using it, eg: check if getResource returns null.
For the first point, it depends on how you build your jar:
Eclipse will by default copy class file and resources to bin unless you use m2e. If you use the Extract runnable JAR (from File > Export menu), it may ignore some resources.
If you use Maven then your images must be in src/main/resources by default.
For the second point, you should use a method that should check the resource exists before delegating to Image. While it won't change your core problem, you would have a less subtile error:
static javafx.scene.image.Image loadImage(Class<?> source, String path) {
final InputStream is = source.getResourceAsStream(path);
if (null == is) {
throw new IllegalStateException("Could not load image from " + source + " path: " + path);
}
try (is) { // Java 9 -> you may want to use InputStream is2 = is
return new javafx.scene.image.Image(is); // use is2 for Java < 9
}
}
You should also try with an absolute path (from the root of the jar, or your src/main/resources if you use maven):
Image image = loadImage(this.getClass(), "/images/man.gif");

Related

Gradle project and resources folder and logical root for creating files

I have a problem understanding how Intellij is working with a Gradle project and the resources folder.
I have created a default Gradle project its created a module 'group', and a module when looking in the module group the src/main/resources folder shows as a 'resource folder' (however it doesn't in the stand-alone module, where groovy/java/resources are all grey).
So that sort out seems to work when compiling code generally.
I tried however to create a file in Groovy script like this
File newFile = new File ("resources/temp.txt")
def fpath = newFile.toURL()
if (!newFile.exists()) {
println "creating new $fpath file "
newFile.createNewFile()
}
However run you run this it fails at bit like this
creating new file:/D:/Intellij - Azure/quickstart-java/graph/src/main/groovy/playpen/resources/temp.txt file
Caught: java.io.IOException: The system cannot find the path specified
java.io.IOException: The system cannot find the path specified
at java_io_File$createNewFile$1.call(Unknown Source)
at playpen.TinkerPop-Example.run(TinkerPop-Example.groovy:47)
The File seems to have relative root .../src/main/groovy/playpen which is where my script is. there is no src/main/groovy/playpen/resources/ so it fails
if a use File("/resources/temp.txt") and look at the URL it shows asD:\resources\temp.txt` defaulting to same drive as where the script is defined.
If you remove the resources prefix - the file gets created in playpen - again assumed root is same as the source program script.
What I want is to read a file from the 'resources' folder but unless I go to absolute file paths it just ignores the 'resources' folder and only looks in the Groovy source folders.
So for example if I copy the temp.txt into the resources folder and run this
File newFile = new File ("temp.txt")
def fpath = newFile.toURL()
if (!newFile.exists()) {
println "creating new $fpath file "
newFile.createNewFile()
} else {
println "reading file at $fpath"
}
it just creates a new temp.txt in the playpen package where the script runs and doesn't see a copy from 'resources' folder.
So what format of 'file name' do I use so that the 'resources' folder is naturally used to resolve file names - without having to use absolute file names?
Equally if want to create a File programmatically and save that in the 'resources' folder where the script runs from src/main/groovy/playpen, what's the path name that puts it in the correct location.
I'm missing something basic here and can't figure out how to read/or write from the resources folder.
ended up with brute force and ignorance on this one - someone may have a 'smarter'answer, but this one appears to be working. Slightly tweaked some code i got working.
I'm using groovy here rather than java (just less boilerplate noise), and nice File groovy methods
steps -
(1)first locate root of your IDE/project using System.getProperty("user.dir")
(2) get the current resource file for 'this' - the class you run from ide
(3) see if the resource is in $root/out/test/.. dir - it so its a test else its your normal code.
(4) set resourcePath now to correct 'location' either in src/main/resources or src/test/resources/ - then build rest of file from this stub
(5) check if file exists - if so delete and rewrite this (you can make this cleverer)
(6) create file and fill its contents
job done this file should now appear where you expect it. Happy to take cleverer ways to get do this - but for anyone else stuck this seems to do the trick
void exportToFile (dir=null, fileName=null) {
boolean isTest = false
String root = System.getProperty("user.dir")
URL url = this.getClass().getResource("/")
File loc = new File(url.toURI())
String canPath = loc.getCanonicalPath()
String stem = "$root${File.separatorChar}out${File.separatorChar}test"
if (canPath.contains(stem))
isTest = true
String resourcesPath
if (isTest)
resourcesPath = "$root${File.separatorChar}src${File.separatorChar}test${File.separatorChar}resources"
else
resourcesPath = "$root${File.separatorChar}src${File.separatorChar}main${File.separatorChar}resources"
String procDir = dir ?: "some-dir-string"
if (procDir.endsWith("$File.separatorChar"))
procDir = procDir - "$File.separatorChar"
String procFileName = fileName ?: "somedefaultname"
String completeFileName
File exportFile = "$resourcesPath${File.separatorChar}$procDir${File.separatorChar}${procFileName}"
exportFile = new File(completeFileName)
println "path: $procDir, file:$procFileName, full: $completeFileName"
exportFile.with {
if (exists())
delete()
createNewFile()
text = toString()
}
}

In Java, why does class.getResource("/path/to/res"); not work in the runnable jar when copying files to the system?

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.

java.util.MissingResourceException - new File() does not reach into jar - how to provide external library with a path for this File?

The question started as - maven does not reach into jar for a folder bundle correctly, when running tests. It does work currectly when running some main() though.
The stack trace for running test (while building) looked like this:
Caused by: java.util.MissingResourceException: Can't find Not found profile: file:\C:\Users\Simon\.m2\repository\ario\TextProcessing\1.0.4-SNAPSHOT\TextProcessing-1.0.4-SNAPSHOT.jar!\lang bundle
at java.util.logging.Logger.setupResourceInfo(Logger.java:1942)
at java.util.logging.Logger.<init>(Logger.java:380)
at java.util.logging.LogManager.demandLogger(LogManager.java:554)
at java.util.logging.Logger.demandLogger(Logger.java:455)
at java.util.logging.Logger.getLogger(Logger.java:553)
at cz.techniserv.ario.tagger.TagDetect.setLangPath(TagDetect.java:126)
at cz.techniserv.ario.tagger.TagDetect.<init>(TagDetect.java:58)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
... 86 more
I triple checked - the lang folder is within the jar.
When running a main() within the module however, it does not try to reach into jar withing m2 repository, but will take the lang folder from within the opened dependency project. So it does not try to reach into a jar, just takes the data from project folder. This runs correctly.
Apparently, the File() constructor is unable to reach for the resource in the jar.
The code, that tries to reach into the jar is within a constructor. It looks like this:
this.path = this.getClass().getResource("/" + aLanguageDirectoryName).getPath();
and than there is a File constructor which takes this path.
In case of running the app it resolves to the project path. When it runs tests it will resolve to the jar within the m2 repository and tries to reach within the path that is in the exception:
file:\C:\Users\Simon\.m2\repository\ario\TextProcessing\1.0.4-SNAPSHOT\TextProcessing-1.0.4-SNAPSHOT.jar!\lang
What would you do? Would you copy the resource "lang" folder on some temporary path and provide the library with this new non-within-jar temporary path, so that it can open with the File() constructor? Or do you see another better way?
Ok, so what we did is that the data are ad hoc extracted to classpath and accessed.
The problem with this solution is that if you delete the files after usage, than you will always extract and delete data with each run - when this happens with every spring initialization of running tests this can happen extreme amount of times and take a lot of time. Leaving the data there however means that if the path is not absolute and out of the project trunk folder, SCM will pick it up unless you ignore it and commit the data which is not satisfying. Also they get packed into the jar upon build since they are on classpath which is another drawback. Yes, you can ignore with SCM and configure maven to exclude the folder, however some developers will forget (one already did) to ignore with SCM and it was commited.
We consider extracting to an absolute path which would not be on classpath a bad practice - firstly because you have no control on what system the projects run on so you cannot guess very well the absolute path - also it does not look pretty to throw data around your computer for bad design.
So I guess the best thing would be to push everyone to place the data on some place on their discs and set an environment variable which would be the same for everyone. This makes the project less portable, requires more configuration, but removes formerly mentioned problems.
I did not come up with anything better.
In case anyone would want to do the same thing, here is our code:
CodeSource src = this.getClass().getProtectionDomain().getCodeSource();
if (src == null) {
return null;
}
URL jarURL = src.getLocation();
try (JarFile jar = new JarFile(jarURL.getPath());) {
Enumeration<JarEntry> enumEntries = jar.entries();
while (enumEntries.hasMoreElements()) {
JarEntry fileFromJar = (JarEntry) enumEntries.nextElement();
File toBeCreatedFileLocally = new File(NAME_FOR_TEMPORARY_FOLDER_TO_HOLD_LANG_DATA_FROM_JAR + File.separator + fileFromJar.getName());
if (fileFromJar.isDirectory()) {
continue;
}
if (fileFromJar.getName().contains(aLanguageDirectoryName)) {
toBeCreatedFileLocally.getParentFile().mkdirs();
try (InputStream is = jar.getInputStream(fileFromJar); // get the input stream
FileOutputStream fos = new FileOutputStream(toBeCreatedFileLocally)) {
while (is.available() > 0) { // write contents of 'is' to 'fos'
fos.write(is.read());
}
}
}
}
} catch (IOException ex) {
Logger.getLogger(TagDetect.class.getName()).log(Level.SEVERE, null, ex);
}
This code is actually a modification of a different answer here https://stackoverflow.com/a/1529707/1920149 (beware, that answer has a bug, check comments - they rejected my edit)

Extract resource folder from running jar in Java 7

My resources folder inside my jar includes a directory with several binary files. I am attempting to use this code to extract them:
try(InputStream is = ExternalHTMLThumbnail.class.getResourceAsStream("/wkhtmltoimage")) {
Files.copy(is, Paths.get("/home/dan/wkhtmltoimage");
}
This is throwing the error
java.nio.file.NoSuchFileException: /home/dan/wkhtmltoimage
Which comes from
if (errno() == UnixConstants.ENOENT)
return new NoSuchFileException(file, other, null);
in UnixException.java. Even though in Files.java the correct options are passed:
ostream = newOutputStream(target, StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE);
from Files.copy. Of course there's not! That's why I'm trying to make it. I don't yet understand Path and Files enough to do this right. What's the best way to extract the directory and all its contents?
Confused because the docs for Files.copy claims
By default, the copy fails if the target file already exists or is a symbolic link
(Apparently it fails if the target file doesn't exist as well?)
And lists the possible exceptions, and NoSuchFileException is not one of them.
If you're using Guava:
URL url = Resources.getResource(ExternalHTMLThumbnail.class, "wkhtmltoimage");
byte[] bytes = Resources.toByteArray(url);
Files.write(bytes, new File("/my/path/myFile"));
You could of course just chain that all into one line; I declared the variables to make it more readable.
The file that does not exist may actually be the directory you're trying to create the file in.
/home/dan/wkhtmltoimage
Does /home/dan exist? Probably not if you're on a Mac.

Locating default root folder for an Eclipse project

I am reading a file as follows:
File imgLoc = new File("Player.gif");
BufferedImage image = null;
try {
image = ImageIO.read(imgLoc);
}
catch(Exception ex)
{
System.out.println("Image read error");
System.exit(1);
}
return image;
I do not know where to place my file to make the Eclipse IDE, and my project can detect it when I run my code.
Is there a better way of creating a BufferedImage from an image file stored in your project directory?
Take a look in the comments for Class.getResource and Class.getResourceAsStream. These are probably what you really want to use as they will work whether you are running from within the directory of an Eclipse project, or from a JAR file after you package everything up.
You use them along the lines of:
InputStream in = MyClass.class.getResourceAsStream("Player.gif");
In this case, Java would look for the file "Player.gif" next to the MyClass.class file. That is, if the full package/class name is "com.package.MyClass", then Java will look for a file in "[project]/bin/com/package/Player.gif". The comments for getResourceAsStream indicate that if you lead with a slash, i.e. "/Player.gif", then it'll look in the root (i.e. the "bin" directory).
Note that you can drop the file in the "src" directory and Eclipse will automatically copy it to the "bin" directory at build time.
In the run dialog you can choose the directory. The default is the project root.
From my experience it seems to be the containing projects directory by default, but there is a simple way to find out:
System.out.println(new File(".").getAbsolutePath());
Are you trying to write a plugin for Eclipse or is it a regular project?
In the latter case, wouldn't that depend on where the program is installed and executed in the end?
While trying it out and running it from Eclipse, I'd guess that it would find the file in the project workspace. You should be able to find that out by opening the properties dialog for the project, and looking under the Resource entry.
Also, you can add resources to a project by using the Import menu option.
The default root folder for any Eclipse project is also a relative path of that application.
Below are steps I used for my Eclipse 4.8.0 and Java 1.8 project.
I - Place your file you want to interact with along the BIN and SRS folders of your project and not in one of those folders.
II - Implement below code in your main() method.
public static void main(String [] args) throws IOException {
FileReader myFileReader;
BufferedReader myReaderHelper;
try {
String localDir = System.getProperty("user.dir");
myFileReader = new FileReader(localDir + "\\yourFile.fileExtension");
myReaderHelper = new BufferedReader(myFileReader);
if (myReaderHelper.readLine() != null) {
StringTokenizer myTokens =
new StringTokenizer((String)myReaderHelper.readLine(), "," );
System.out.println(myTokens.nextToken().toString()); // - reading first item
}
} catch (FileNotFoundException myFileException) {
myFileException.printStackTrace(); } } // End of main()
III - Implement a loop to iterate through elements of your file if your logic requires this.

Categories