Using the following code to set a system property:
ClassLoader classLoader = StartMain.class.getClassLoader();
URL resource = classLoader.getResource("com/myname/lib/chromedriver/chromedriver.exe");
File f = new File("Driver");
if (!f.exists()) {
f.mkdirs();
}
File chromeDriver = new File("Driver" + File.separator + "chromedriver.exe");
if (!chromeDriver.exists()) {
chromeDriver.createNewFile();
org.apache.commons.io.FileUtils.copyURLToFile(resource, chromeDriver);
}
This works perfectly when I run my application configuration in my IDE, IntelliJ. However, when I build a JAR and attempt to use this outside of IntelliJ, the resource URL returns as null. Why is this so?
Going via your classloader is risky. It probably doesn't explain your problem but it might; in any case, this alternative way to do it is shorter, simpler, more idiomatic, works in all places your take works, and works in more places to boot:
The best way to fetch resources like this is like so:
StartMain.class.getResource("/com/myname/lib/chromedriver/chromedriver.exe");
Note that this one starts with a slash! This style goes relative to your own class file location (your package, basically) if you don't.
Either form will look for the entry:
/com/myname/lib/chromedriver/chromedriver.exe
inside the same jar that StartMain.class lives. If it is not there, then this obviously won't work - fix your build so that it is included. At 'runtime' some folder may be on the classpath that would resolve this file; if that folder is then not folded into you jar during the build, that would explain why it works within the IDE but not elsewhere.
NB: You generally don't need any apache utils. For example, there's InputStream's transferTo which can make this a one-liner too (fetch getResourceAsStream instead).
Related
I tried to create an InputStream pointing at an .ico file, which is in a directory in the src directory. I also create an InputStream for a different jar file in my src.
InputStream inIco = Installer.class.getResourceAsStream("/res/" + iconName + ".ico");
InputStream inApp = Installer.class.getResourceAsStream("/res/" + applicationName + ".jar");
that is how I tried to load it. The inputstream for the jar file works, but the other one is null.
Edit: Sorry for the confusion guys. I didn't build the jar, I just ran it from my editor, which obviously gives me different results, now it is working. Thanks for your answers.
getResourceAsStream (gRAS) loads from the same place java loads class files. That's great - it means you can ship your app as a jar and put these resources inside. If it's not working for you, you've misconfigured your build. Specifically, java is first going to determine the classpath root of your Installer.class file and looks there. If you're not sure what that is, run this code:
System.out.println(Installer.class.getResource("Installer.class"));
which will print something like jar:file:/Users/carlos/projects/FooBar/dist/foobar.jar!com/foo/Installer.class
and this tells you that gRAS is going to look in that foobar.jar file.
From there, the resource is loaded relatively to the root (because of that leading slash): Within that jar, it will look for /res/app.ico. Without it, it loads relative to the same dir/package of Installer class (in this example above, gRAS("hello.txt") is the same as gRAS("/com/foo/hello.txt").
To make this work out, your build system is responsible. For maven and gradle, have src/main/java/com/foo/Installer.java along with src/main/resources/res/icon.ico and all should be well. If this is not working out, explain how you've set up your environment because something is misconfigured if this isn't working. If you're using another build tool (such as perhaps ant, sbt, or relying on your IDE to take care of it), name the tool and perhaps we can help address the misconfiguration.
I have written a code that is packed to 1.jar
with this code:
return isProd? "/etc/waze/automation/devices.json":
DeviceRepositoryFromJsonFile.class.getClassLoader().getResource("devices.json").getPath().toString();
devices.json is here:
I have another project that depends on 1.jar
however the classLoader doesn't find the local devices.json file but rather one packed in the jar
anyhow it shows the file doesn't exist.
How can I fix this? just use a absolute path anyhow?
If as in your screenshot the devices.json locate in the src/main/resources and the package have successfully treat that as the package path and put in the jar file root directory, then you can just find the file via:
DeviceRepositoryFromJsonFile.class.getResource("/devices.json");
Note the "/" slash is important to indicate that to search from the root of the classpath.
It does not answer your question directly, but it may solve your problems faster.
As far as I can see you try to detect the absolute path to json file and pass it to another method so this file could be processed. Instead, it could be done simpler:
public byte[] getDevicesJsonBytes() {
return isProd
? IOUtils.toByteArray(ABSOLUTE_PATH_TO_PROD_FILE)
: IOUtils.toByteArray(DeviceRepositoryFromJsonFile.class.getResourceAsStream(RESOURCE_CLASSPATH);
}
The common way to read classpath resources it to use getResourceAsStream on class or classLoader instance. Also, many frameworks have their own resources abstractions, but I guess you don't need them now.
I know the same question has been asked many many times, but going through all the answers already posted I couldn't find a solution.
First of all here's the code I'm using to test the issue after i encountered it in my project :
InputStream test;
System.out.println(this.getClass());
System.out.println(("classpath is: " + System.getProperty("java.class.path")));
test = getClass().getResourceAsStream("/pairs/images/100/back/back1.png");
if (test == null)
{
System.out.println("getClass().getResourceAsStream(\"/pairs/images/100/back/back1.png\") is null");
test = GridPanel.class.getClassLoader().getResourceAsStream("pairs/images/100/back/back1.png");
}
if (test == null)
{
System.out.println("GridPanel.class.getClassLoader().getResourceAsStream(\"pairs/images/100/back/back1.png\") is null");
test = ClassLoader.getSystemClassLoader().getResourceAsStream("pairs/images/100/back/back1.png");
}
if (test == null)
{
System.out.println("ClassLoader.getSystemClassLoader().getResourceAsStream(\"pairs/images/100/back/back1.png\") is null");
Thread.currentThread().getContextClassLoader().getResourceAsStream("pairs/images/100/back/back1.png");
}
if (test == null)
System.out.println("Thread.currentThread().getContextClassLoader().getResourceAsStream(\"pairs/images/100/back/back1.png\") is null");
So as per title everyone of these calls to getResourceAsStream() returns null, but only when executin the jar file, when launching inside the IDE (Netbeans 8.0.2) they all return the correct stream (tested individually not with this piece of code) and I can work with it.
The jar file contents are as follow:
While the src folder in the netbeans project folder is the following:
So I'm really baffled at the moment, I've tried using the default settings when building with netbeans but I'm losing my head around this issue.
Any hints or ideas would be much appreciated!
I think the getResourceAsStream is not reading from filesystem, the leading slash will try to read from the root of the classpath (if you add a directory will read from the root). If you don't put the leading slash, you will read from the package the class is from.
Did you add the directory/subdirectories you want to read the file from in the classpath?
TL;DR It was due some stupid issue with NTFS permission, moving the jar made it work
So I feel very stupid right now...
I tried running it in a Linux environment and everything worked as supposed.
Each one of those calls was returning a non-null InputStream pointing to the resource.
So I did something very simple: just moved around the compiled jar in Windows to check if it had something to do with NTFS permissions (keep in mind that I'm using an administrator account and I can freely write, read and execute in those folders).
Moving it to the root of the project didn't work, but moving it on the desktop WORKED.
What amazes me more is that in some other parts of the projects I'm doing
URL jarUrl = PairsDeck.class.getProtectionDomain().getCodeSource().getLocation();
to read the whole content of the jar file and that works every time even in the old directory, so that's why I hadn't thought of screwed permissions since the beginning (and I was launching it from a command prompt with admin privileges).
So I guess somehow the program can read its jar with that call but not load resources from it inside my netbeans project folder due to some issues with NTFS permissions.
To add some confusion
File file = new File(jarUrl.toURI());
file.canWrite();
file.canRead();
file.canExecute();
all of the above return true.
I'm just curious to know if there was a way to understand better the issue and maybe have a way to signal some permissions error from the java code.
Thanks wholeheartedly to everybody involved for the help!
There are two problems here.
Windows is case-insensitive. Other platforms and inside a jar names are case-sensitive.
The getClass() class determines in which jar/class path the resource is souught. Especially a unit test will not be put in a jar, or a child class might be located outside the jar. Either use a SomeClassInJar.class or use a ClassLoader which searches over all jars/class paths. A class loader uses only absolute paths, and you must not start the path with a /.
This is the well known problem of loading resources from a jar file. This is not the first time I've tried to do this, but now it doesn't work the way I expect it to.
Normally I try to load the Resources with this.getClass.getResource("foo.png"), or getResourceAsStream()and it works. Now however it does not. The Resource is always null.
If I let System.out.println(this.getClass.getResource("")) print me the path (from eclipse) it shows /path/to/eclipseproject/package/structure/. Running this from a jar it just shows rsrc:package/structure
If I recall correctly this should print the path to the jar. Furthermore I thought this would print the package structure in both cases. Am I doing something wrong?
Here is the thing...
When Extracting the file from the Jar use:
this.getClass.getResource("/foo.png")
When running from a runnable Jar use, to reference an external file in the Jar folder path:
this.getClass.getResource("foo.png")
// When running this from Eclipse, it would refer to files in project root!
I have a code in the lower level determining where I'm running from to determine the correct path.
Doe this get the path you need?
this.getClass().getClassLoader().getResource("<your class name>.class").getPath();
See also this question for more on this issue.
Unless you prepend the path to the resources with '/', Class.getResource() will search for the resource in class package. E.g.: tld.domain.Foo.class.getResource("Bar.txt") will search for tld/domain/Bar.txt
Check the URLClassLoader for all the gory details, but it really depends on whether you are trying to access a ressource in the jar,
using a class loaded inside the same jar, in this case your file 'root' is the root of the jar
using a class loaded outside the jar (your eclipse case) where the root is your 'working directory'
To access resources inside a jar from outside, you should use something like
URL url = new URL( "jar", "", "file:" + jar.getCanonicalPath( ) + "!/" + localPathResource );
url.openStream(...)
This answer provides an explanation of how to load class resources from JAR files, even when the class is not in the JAR file and not in the Class-Path specified in the JAR file's manifest. There are also links to code.
I'm newbie to java.
I have some directory structure
product/
conf/
classes/com/../..
conf/ contains some configuration file, while under classes/ I have my application.
How can I ensure from inside java code that I'm able to find file in conf/ despite way I'm executing it (e.g. from eclipse, from different directories, from crontab etc.).
P.S.
Files in conf/ are not resources, since required to be edited by user.
Is there're way to know where my .class, so I canuse relative path form that directory to reach my directory (e.g. MY_CLASS_DIR/../../../../conf)
I would put the conf directory into the class path. That way you can always find them by:
YourClass.class.getClassLoader().getResource("conf/....");
You can use the absolute path, including the way to product.
Or you may use a configuration setting, by starting your program like
java -DXY_HOME=/some/path/product ...
From the javacode, you use it:
String xyHome = System.getProperty ("XY_HOME")
Or you use a kind of inifile in your home directory, where you specify where to look for the conf-directory.
Rereading your question multiple times, it is unclear to me what your goal is. To find the conf dir independently from where you are (eclipse, crontab, ...)? But the headline asks for the CWD, which is the opposite - the directory, depending on where you are.
Both is possible, but you have to decide what you want.
Its safe to use relative paths than absolute paths. Even if you JAR your classes tomorrow it will work as is,
Put you configuration files in classpath during deployment.(Please note that
project directory structure can be different from that of deployment directory structure)
product/
classes/com/../..
classes/conf/some_conf.properties
Then you can use Apache common configuration to get the URL of file
URL urlOfFile = org.apache.commons.configuration.
ConfigurationUtils.locate("conf/some_conf.properties");
The other alternative you can try is,
URL urlOfFile = <SomeClassFromClassesFolder>.class.
getClassLoader().getResource(resourceFile);
Once you get the URL of your configuration file getting stream out of it very simple,
InputStream stream = urlOfFile.openStream();
Good luck.
For you understanding you can refer the following as well,
http://bethecoder.com/applications/tutorials/showTutorials.action?tutorialId=Java_IO_CurrentWorkingDirectory
http://bethecoder.com/applications/tutorials/showTutorials.action?tutorialId=Java_Reflection_WheretheClassloadedfrom
Good luck.
you can find out what is the absolute path of the working dir by:
String str = new File("").getAbsolutePath()