How to make File class use relative paths? - java

I've been working on a project in java for a while now. I realized something terrible when trying to run a .jar of the project: It wasn't using relative paths anymore, instead it was searching in the "C:\Users\Username\..." directory for files.
I read that instead of using new File("..."); you should use getClass().getResourceAsStream("...");
In a lot of places I used things like new Sprite(new File("sprites\\sprite.png"));, so it would take a very long time to go in and replace all of them.
I was hoping that there was something else I could do instead.
Like, is there any way to change the behavior of the File class?
Any tips? Thanks.
EDIT:
I tried Duran Wesley Harris's idea of setting the user.dir property to the path of the folder with the .jar, which actually successfully changed where the files were being looked for. But the program still fails to load the files. I can actually copy the directory it's using into File Explorer and load the file that way, so it's mysterious to me why it can't load it in the program.
Note: The files are not inside the .jar, they are sitting next to it in the same folder. Also, the program works perfectly in the IDE (IntelliJ Idea).

Personally, I think you should take the time to refactor your code to use xx.getResourceAsStream() methods.
If you want to try and make a hacky solution you could try setting the "user.dir" property to the root of the jar with System.setProperty() or java -Duser.dir="" via command line.

Related

Why can't I create a folder in %CATALINA_TMPDIR%?

This is the weirdest thing ever. I'm working on an app that I run using Tomcat though IntelliJ IDE. Part of this app creates a folder in the configured %CATALINA_TMPDIR%. For some reason no matter where I point %CATALINA_TMPDIR% to java can't create a folder there, but it can create a folder everywhere else.
For example, If
CATALINA_TMPDIR=C:\Users\dallend\.IntelliJIdea14\system\tomcat\Unnamed_datasite_7\temp then my app can't create a folder there, but can create it in C:\Temp, a folder I created.
Then if I change to CATALINA_TMPDIR=C:\Temp, the app won't be able to create a directory there but will be able to create one at C:\SomewhereElse.
From debugging through things it appears that java.io.File tries to create the directory but that the WinNTFileSystem.public native boolean createDirectory() method can't successfully create it.
Why might this happen? Could there be something I need to tweak with IntelliJ, or Java, or my OS? Has anyone encountered this before?
EDIT: I've tried running Tomcat stand-alone, and the problem persists.
Try to use forward slashes in your directory names. I've pulled out a lot of hair with backslashes actually escaping a path, e.g. C:\temp pointing to C:<tab>emp. I've completely given up on using backslashes even on Windows: Java will translate forward slashes just fine: C:/temp/

Packaging an Eclipse Program With External Files

I'm not sure if this was answered previously, I tried searching for the kind of question I am asking, but I couldn't find something satisfactory. If someone could point me to a similar question, that would help.
What I am trying to do is to package an eclipse program that has external files, such as images, into a single file, rather than a jar file and the supporting files placed in the same directory.
I am not sure if that is even possible, but is there some way in eclipse that would allow you to somehow package the external files along with the jar file in a single, neat file that can be executed easily?
EDIT:
Thanks to ortis and Thorbjørn Ravn Andersen, I figured out how to do it. Here are the steps for working it out:
Firstly, ensure that all of your external files are in your src folder, not in your bin or workspace or whatever else. If you have packages in your project, they will appear as folders and the external files must be placed into those folders for use within that package.
As for calls to the external files, don't do something like this:
Image img = new Image("fileName");
Rather do this:
Image img = new Image(getClass().getResourceAsStream("fileName"));
while ensuring that "fileName" is in the src folder or package folder, if you have packages.
Some points:
-If you are initializing a global variable using a file name that is outside of your main constructor or function call, getClass(), won't work. Use:
yourClassName.class.getResourceAsStream("fileName");
-if your using JavaFX Scenebuilder and you need to package your external CSS file, here is a solution that will help you load the style sheet in your program instead of using the .fxml file to do it.
-An InputStream variable can be assigned the result of
getClass().getResourceAsStream("fileName"));
and be used instead of the whole mess.
Yes. You must reference all external files as inputstreams instead (because you want the classloader to get them from inside a jar file), and then wrap the resulting single jar file as an EXE file using http://launch4j.sourceforge.net/

How to access Jar file located within source folders?

in my Java project I am using an H2 in-memory database, for which I have to load the JDBC driver when I initialize my application. I want/need to load the H2 .jar file dynamically, so I do the following:
String classname = "org.h2.Driver";
URL u = new URL("jar:file:libs/h2.jar!/");
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver) Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));
When I put the H2 .jar file into a "libs" folder outside my Java source code folder (that is, in Eclipse, this "libs" directory is on the same level as the "src" folder), then this approach works fine. However, unfortunately I have to put this H2 .jar file into a folder within the source code folder tree, but below the main class folder.
For example, my Java package structure looks like this in Eclipse:
<project>/src/my/app/MyApp.java // main class of my application
<project>/src/my/app/sub/package/h2.jar // how to access this?
<project>/libs/h2.jar // loading from here works
I know this is stupid, but unfortunately I have to work with this strange setup. But what I don't know: how can I edit my Java code (listed above) in order to work with this setup?
EDIT: This has to work outside Eclipse as well, so adding the JAR file to the Java Build Path in Eclipse is no option for me.
EDIT2: I already tried to load "jar:file:my/app/sub/package/h2.jar!/", but that did not work for me.
Thanks in advance for all helpful ideas!
Kind regards, Matthias
In some frameworks referring to files inside JARs can be done using the classpath: prefix. I doubt URLClassLoader supports it natively, but it's worth a try (e.g. classpath:/my/app/sub/package/h2.jar). But since that doesn't work with URLClassLoader, here are other ways:
One way to do it would be to write your own ClassLoader which reads the JAR file from classpath (using getResourceAsStream), uncompresses it (using ZipInputStream) to memory (e.g. a map of byte arrays) and loads the classes from there.
Another, slightly easier way, is to read the JAR file from classpath and write it into a temporary file. Then you can use the plain URLClassLoader to load classes from it. This has the disadvantage that the file must be written to a file and the file probably cannot be removed until the JVM exits (unless using Java 7 or higher).
I'm using the second approach (copying to a temp file) in one project, though I'm using it to launch an external process. I would be curious to hear why you have such a requirement. If it's just a matter of having the whole application in one JAR, there are numerous simpler methods for achieving that (Maven Assembly Plugin, Maven Shade Plugin, Jar Jar Links, One-JAR to name a few).
No it's not a homework, but an online build system that uses my classes under my/app/* and several other classes (not from me) to automatically build the whole solution. Anyway, I can't give you more details on the internals of this system, as I don't know them. As said, I simply have to live with it, and that is why I am asking here...
Sounds like you are working in a WTF environment (does it have a name?), so here are some ways to start hacking around it:
Find out more about your environment, especially absolute file paths of the following: directory where the source files are saved, directory where the generated .class files are saved, and the current working directory when the program is run.
If you can get any kind of output of what your program prints during runtime, you can put into your application some debug code where you use File.listFiles() to crawl the machine's directory trees. If you can get output only from what happens when compiling, it might be possible to execute your own code during compile by creating your own annotation processor (apt is part of javac since Java 6), though I'm not sure whether the annotation processor must be compiled first separately.
The working directory can be read from the user.dir system property and the location of class files can be probably gotten from the java.class.path system property (unless custom class loaders are used). There is no guarantee that a JAR file in the source directory would be copied to the classpath, so you might need to do some looking around.
Then when you know the file path of the JAR file, then you can get an URL to it using new File("path/to/h2.jar").toURI().toURL() which you can then pass to URLClassLoader.
If nothing else works, upload the source code of the libraries and compile them together with your project.
In the long run, try to replace the WTF build environment with one that uses a standard build tool (such as Maven) and a common CI server (such as Jenkins). It's normal for projects to have lots of library dependencies, so you shouldn't need to hack around a build environment to use them.

Exporting Images with JAR in Eclipse (Java)

I've been working on a little project that requires external images for display. I'm not all that familiar with how to use Eclipse and this is my first time attempting to export a completed project so I can share it with others. Right now, it seems the only way I can get my images to show up is if I assign a specific folder on my hard drive and have the image paths in the code go to that.
I'm looking for a way to export the images as part of my JAR or as part of the same package so when I go to send this program to other people, I don't have to send them a separate archived folder of images. I'd also be interested in learning what I need to do to have my code reference the images within that package so they'll work without an external folder.
I have read about some kind of package system within Eclipse, but have thus far had no luck in figuring out how to use it. Could use some explicating!
Thanks in advance to anyone willing to give me their two cents.
Something I would have found useful with this answer is the following: make sure you put your images/files in the same eclipse folder (or sub-folder below) as your source code. I created a folder "images_ignored" using eclipse, added it to the build path but still it refused to be included in my JAR file (when creating an executable JAR).
Just drag the images folder into your Eclipse project, then choose to "Copy New Folder" or "Copy File and Folder" depending on Eclipse version, and then right click on the image folder (in Eclipse) and --> build path "use as source folder".
you might need to load them as class path resources if they are within a jar. see: getClass().getClassLoader().getResourceAsStream(...)
Use getResource() to load the images:
ImageIcon qmarkIcon = new ImageIcon(getClass().getResource("images/mark.gif"));
If you're using JDK 1.7 or JDK 1.8, you might want to use the NIO.2 API.
for (FileSystemProvider provider : FileSystemProvider.installedProviders()) {
if ("jar".equals(provider.getScheme()))
return provider.newFileSystem((new File(Start.class
.getProtectionDomain().getCodeSource().getLocation().toURI()))
.toPath(), new HashMap<String, Object>());
}
If you enter this code into a method that returns a java.nio.file.FileSystem, you can call the method to get the FileSystem for the JAR file.
To get the path to access the files inside your JAR file, you can use the following method, which then allows you to read the files however you may want.
fileSystem.getPath("/images/image.gif")
If you would like to be able to run this in Eclipse, make sure you surround the call to the method with a try/catch IOException and assign to your FileSystem object the following.
new File(Start.class.getProtectionDomain().getCodeSource().getLocation().toURI())
.toPath().toString();
This will allow you to run your program whether it's compressed into a JAR file or not.
I recommend you get used to using NIO.2, since it is a very powerful API.
If you add a folder to build path you can retrieve the images either in eclipse and when you exported it in jar file, just remember to don't reference the image with the path like img/myImage.gif but only myImage.gif !

How do you get file path for file when user directory changes?

I have a java app, and the log4j.properties file is in src/com/my/path/props. On compile, it's copied into classes/com/my/path/props
The file is loaded via PropertyConfigurator.configureAndWatch(user.dir + "/classes/com/my/path/props/log4j.properties").
This all works fine normally, though it's not ideal because of using user.dir (but I do not know another way to reference a file relative to the "application's start directory"). The problem manifests when trying to run this application using an NT Service wrapper. When done this way, the user.dir changes from the application's root dir to wherever the NTService wrapper's exe file is.
My question is: What's the appropriate way to get a the String file path representation of the log4j.properties file in my classes/com/my/path/props/ directory? I realize this would completely break down if the props file were in a jar; but in this case, it's not and is simply a file on the file system.
I've tried new File(this.getClass().getClassLoader().getResource("com/my/path/props/log4j.properties").getURI()).getAbsolutePath(), but that fails because on production, the path to the file is actually a UNC path and consequently throws a "URI has an authority component" exception.
How do other people deal with this problem?
Thanks.
OK. So... you asked how other people deal with this problem. First, they do not leave it up to Eclipse for where files get placed. They choose where they want them, how they want to access them, and then have their build tool (which unless they are just playing around, should not be an IDE like Eclipse, but rather a dedicated build tool like Maven or Ant) where to place it.
The choice of where you want the file depends on what you want to do with it. If its simply a config file that will never be edited at runtime, you typically place it inside your JAR (which is another practice - applications are placed in one or more JARs, WARs, or EARs, not a classes directory). If the file is to be edited at runtime, which from your "watching" it appears to be the case, you typically put it in a config directory outside your JAR.
How you access it (from the filepath or the classpath) is another choice. Where possible, I favor accessing files from the classpath because it is more portable - and when in a JAR, pretty much required. If that doesn't make sense in your case, then choose a path other than "user.dir" if that is changing when you deploy. You can hard-code it, use an environment variable, a property, a config file, a command line argument, etc. to set the actual path.
Always choose where things go and how you access them. Don't let your tools choose for you. It will make your life easier :-)
I took singleshot's advice and kept the properties files out of src and instead in a separate directory which I added to the classpath. In retrospect, this was indeed boneheaded to have configured it the way I did originally.
From there, my problem was getting a File from a URL. I ended up finding what I needed in Commons IO FileUtils, with its toFile(URL) method.
The code ended up looking like this:
private URL maintenanceConfigPath = this.getClass().getClassLoader().getResource("MaintenanceConfig.properties");
....
File f = FileUtils.toFile(maintenanceConfigPath);
....
Again, thanks to all for your feedback and for pointing me down a path that got me towards an answer

Categories

Resources