I have a web application running under tomcat 7, and in one of the class, Im trying to read a file in one of the jar under WEB-INF/lib folder.
URL resourceURL = MyClass.class.getClassLoader().getResource("xml/xslt/master.xsl");
File xslfile = new File(resourceURL.getPath());
AssertUtil.assertTrue(xslfile.exists(),"xsl file not found");
Both MyClass and master.xsl resides in the same jar and there is no issue with packaging. But above snippet fails in the assertion statement as xslfile.exists returns false. The URL correctly resolves to the location of the file inside the jar as given below
file:/<MY_WEBAPP_LOCATION>/MyApp/WEB-INF/lib/MyComponent.jar!/xml/xslt/master.xsl
where MY_WEBAPP_LOCATION corresponds to the absolute path to my tomcat servers webapp directory.
But if I rewrite the code as below to read as inputstream, it works fine.
InputStream xslFile = MyClass.class.getClassLoader().getResourceAsStream("xml/xslt/master.xsl");
Can anyone explain what is preventing the creation of File from the jar resource, whereas the inputstream creation is working perfectly fine. Is there any additional permission settings needed from tomcat side, to read a file inside jar ?
EDIT: One more observation, if the file is placed under WEB-INF/classes, creation of File with above code works fine. Issue is only when it is placed in a jar under WEB-INF/lib
Be careful it seems that ClassLoader.getResource does not handle relative path.
See this.
GetResourceAsStream happens to take the path relative to the ClassLoader (and not the class !!). I think you're lucky enough that there are the same here.
If it is a Desktop application getResource() will work
But as this is a web application the resource needs to be extracted from Context , hence getResoruceAsStream()
It is not a permission problem, but the use of java.io.File API - in particular constructor http://docs.oracle.com/javase/7/docs/api/java/io/File.html#File%28java.lang.String%29
When you are constructing File object using
File xslfile = new File(resourceURL.getPath());
you are using java.io.File#File(String) method which expects an "abstract pathname". What is an acceptable/valid pathname is described by javadoc of the File class: http://docs.oracle.com/javase/7/docs/api/java/io/File.html
String value that your are getting from getPath() method:
file:/<MY_WEBAPP_LOCATION>/MyApp/WEB-INF/lib/MyComponent.jar!/xml/xslt/master.xsl
simply does not constitute a valid "abstract pathname" - it is a URL that is converted to a java.lang.String (and IMHO should be returned with URL scheme of "jar" and not "file"). Therefore a call to
isExist()
returns false as there is no file with such name on your disk.
On the other hand if the resource is outside of a jar (e.g. under WEB-INF/classes directory) resourceURL.getPath() will return a value that presents a valid abstract pathname as the resource in question is indeed a simple file.
When you use java.lang.ClassLoader#getResourceAsStream(java.lang.String) the method streams out the resource directly into a java.lang.InputStream and might not even use File class in its implementation.
Related
I use web fragments (servlet 3 spec) and thus can load e.g. META-INF/resources/access.xml file which is in a library in /WEB-INF/lib/ of my WAR via ServletContext.getResourceAsStream("access.xml").
Doing the same with ServletContext.getRealPath("access.xml") doesn't work (=> null).
The spec states:
The getRealPath method takes a String argument and returns a String representation of a file on the local file system to which a path corresponds. Resources inside the META-INF/resources directory of JAR file must be considered only if the container has unpacked them from their containing JAR file when a call to getRealPath()
is made, and in this case MUST return the unpacked location.
My container (Tomcat) didn't unpack the jars, this seems to be the problem? How can Tomcat unpack the jars. Should I unpack the jars when packaging the WAR?
So it depends on what you are trying to do. If you are trying to get the location of the file in the included library dependency, then I am not sure. If you are wanting to read the contents of the access.xml file then you can use
new InputStreamReader(context.getResourcesAsStream("access.xml"));
and then work with the InputStreamReader to get the file's contents.
Yes, getResourcesAsStream() always works. But getRealPath() doesn't.
Answer: Don't try to use getRealPath() together with web fragments.
I tried everything to access to CSV file when a run my jar. I put the CSV in resources package in Eclipse, and it's fine when I run the code from there, but it doesn't work when I run the jar from an executable.
ClassLoader c = MyClass.class.getClassLoader();
URL url = c.getResource("com/mysoft/resources/");
String path = URLDecoder.decode(url.getPath(), "utf-8");
File f = new File(path+ "VAL.csv");
if(f.exists())
...
I don't want to put this file out of the jar. I just want read the file. How can I do it?
EDIT: I closed my login of my first post - Access to csv file in jar - and I don't know how to remove it, and I can't respond to comments. So I posted it again here.
Is it possible not to use InputStream ?
Your use of getResource() here is very dubious. I would not rely on getResource() finding a packet/directory at all (after all, one can argue that it should return resources, not namespaces).
Also, never ever interpret the URL returned by getResource() - its inviting trouble times two. The ClassLoader can return you anything, the URL returned may have nothing in common with what you asked for. Constructing something from the URL's String representation has an abhorrent opportunity of failure when the ClassLoader isn't your standard classpath JRE classloader (think of Application Server, WebStart etc.).
Ask directly for the Resource you want:
getResource("com/mysoft/resources/VAL.csv")
And don't create a File, use InputStream. Resources aren't files, you can not access them with File API.
Synopsis
Given the following (abridged) code that lives server-side on a servlet (Tomcat is the container). This is a GWT application, though that should be irrelevant (I think).
ServletContext context = getServletContext();
String dataFilePath= context.getRealPath("/WEB-INF/dir/dataFile.txt");
File dataFile = new File(dataFilePath);
TestCaseGenerator testCaseGenerator = new TestCaseGenerator(dataFile);
testCaseGenerator.generateTestCase();
TestCaseGenerator is a class from a jar in the project's war/WEB-INF/lib folder, that's been added to the GWT project as an external library.
The Problem
When testCaseGenerator.generateTestCase() gets executed, it's unable to use dataFile to create a new LineNumberReader(new FileReader(dataFile));, a FileNotFoundException gets thrown.
I've verified that the String value of dataFilePath is correct and contains the proper real path to the file on the server I need to read from, as well that dataFile isn't null. I've also verified that TestCaseGenerator runs just fine when called from a command line, outside of this GWT application.
I'm not sure why TestCaseGenerator isn't able to use the File object I pass it, considering I'm passing it the real file path of the file. I can come up with some alternative solutions to get around this issue, but now I'm genuinely curious why it isn't able to find the file.
Thanks in advance for any insight.
Solved
I foolishly neglected to include the dir/dataFile.txt in my build.xml's war target, so in fact the file wasn't being included in the war package, and thus never placed within the Tomcat container.
<include name="dir/**" />
And I also misunderstood what context.getRealPath("/WEB-INF/dir/dataFile.txt"); actual returns;. From ServletContext javadoc
Gets the real path corresponding to the given virtual path. For
example, if path is equal to /index.html, this method will return the
absolute file path on the server's filesystem to which a request of
the form http://://index.html would be
mapped, where corresponds to the context path of this
ServletContext.
So even though getRealPath returns a path value, it doesn't necessarily mean the file is at that actual path.
I'm trying to have my application load a resource (binary file) transparently:
If the file exists under the current directory, open it.
If not, try looking in the current JAR file if applicable.
If not, try looking in other JAR files. (This is optional and I don't mind explicitly specifying which JAR files.)
So far I know of File which opens a local file and ClassLoader which has getResource* for JAR contents.
Is there a class which combines the two? If not, how should I go about writing it myself? Should I write a ClassLoader which also checks the local filesystem? Using File? (I'm very unfamiliar with Java and don't even know what's a good type to return. InputStream?)
Thanks
P.S. By "file" I mean "path", e.g. "data/texture1.png".
Doing #1 and #3 is pretty easy. Doing #2 (just looking in the current JAR only) is much harder as it requires you figuring out what JAR you
If you wanted to check the filesystem first, otherwise load from classpath, it would be something like:
public java.io.InputStream loadByName(String name) {
java.io.File f = new java.io.File(name);
if (f.isFile()) {
return new FileInputStream(f);
} else {
return getClass().getResource(name);
}
}
If you want to prefer loading from the same JAR file first, you will need to figure out where it is. Check out Determine which JAR file a class is from for more info on figuring out the JAR file you want to load the resource from.
A URLClassLoader should be able to load both and try the file path first if the file path is on the class path ahead of the jar.
Regarding your comments:
I know that relative jar URLs don't
work. That's why the Spring guys came
up with the Resource abstraction.
Read about it here.
You might want to check the answers
to this Question: Loading a file
relative to the executing jar
file. The problem is similar to
yours.
Current jar file and current directory are not concepts in the JVM like they are when you're running a shell script. You would need to specify a directory to be used for loading the files that you're interested in, such as with a system property while executing the JVM:
java -Ddirectory.to.scan=/home/aib
Then retrieve this property:
String dir = System.getProperty("directory.to.scan");
Now when talking about JAR files, all JAR files specified explicitly on the classpath when you start the JVM are loaded by the ClassLoader. You can get the ClassLoader of a specific class by:
InputStream is = <Your class>.class.getClassLoader().getResourceAsStream("binary file");
Note that any jar file loaded by the current class loader is searched.
I'm writing a Java application that needs to access several resources in a .jar file that is run over JNLP.
While the application works fine in my development environment (Eclipse) it doesn't work when executed through JNLP, apparently because it can't find the resource file in the jar. I've checked the resource file and the resources are most definitely there.
I'm currently using code like:
someclass.getResourceAsStream("/resources/somefile.png");
What is the correct way to access a resource file in a .jar that will work with JNLP?
use : this.getClass().getClassLoader().getResourceAsStream(name)
example: myClass.getClass().getClassLoader().getResourceAsStream("resources/somefile.png")
two tips:
1 - use your own class that is in jar file. if used another class - for example Object - fails
2 - name i.e. resource must be without leading '/'
I got stuck for a while on a similar issue and the comment from #Devon_C_Miller saved me (once I saw it, after some time!), so I thought I'd recopy it here:
When you get a resource via a Class, the path is resolved relative to the class, unless you start it with a '/'. When you get it via a ClassLoader, it is always resolved as an absolute path and must not begin with a '/'.
In my case, I use the following syntax, for a file located in the JAR: /properties/config.properties:
//NO LEADING `/` EVEN IF IT IS AN ABSOLUTE PATH
private final static String CONFIG_FILE = "properties/config.properties";
InputStream resource = Configuration.class.getClassLoader().getResourceAsStream(CONFIG_FILE);