I'm writing a java servlet that calls a function in a jar. I have no control over the code in this jar. The function being called wants the filename of a configuration file as an argument.
I'd like to bundle this file with my war file. If I put it in the war somewhere, what filename can I pass the function in the jar?
Note that only a filename can be used with the jar's API. So ServletContext.getResourceAsStream() is not helpful.
Use ServletContext.getRealPath(). This returns the filesystem path for a given servlet context resource. You pass it the same argument as you would pass to ServletContext.getResourceAsStream()
Better yet, use Spring's Resource abstraction:
Resource resource = new ServletContextResource(servletContext, "/path/to/file");
File resourceFile = resource.getFile();
If it's in your .war file, you won't be able to access it as a file. Some servlet containers will explode a .war file into components, but I don't think you can rely on it.
Have you thought about extracting it (via getResourceAsStream()), writing it to a temp file/directory, and then referencing that ?
Related
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.
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.
Suppose I had a directory containing resource files stored somewhere within the "src" source directory, containing things like templates, config files, etc.
I'm aware that from a Servlet I can access files by name like:
File file = new File(ServletContact.getResource("some/namespace/filename.txt").getPath());
And from a non-Servlet I can do:
File file = new File(Object.class.getResource("some/namespace/filename.txt").getPath());
But the problem is that I have code that needs to access these resource files and can be run independent of the runtime environment. e.g. Some code uses templates from within a servlet (under Tomcat 7). Other code runs as a Quartz background job and works with templates. If I try the Object.class.getResource() method in a Tomcat servlet, it returns null.
How can I access resources files in a safe way regardless of runtime environment, app engine, etc.?
To read file from classpath you can use:
getClass().getClassLoader().getResourceAsStream("path/to/resource");
Also there is simple and useful Spring utility ClassPathResource class:
Resource resource = new ClassPathResource("path/to/resource");
I would use any class (e.g. domain class) from your project, use getClassLoader() or getContextClassloader() and provide the path to your resource. Should work.
I'm working in Maven web application (Java). I need to read a directory (for ex: mydirectory) in my webapp folder as follows:
File file = new File("path of my directory");
I just specify the hierarchy of folders in my application.
MyApplication->src->main->webapp->MyDirectory
But I'm going to write my Java code I the package as follows,
MyApplication->src->main->java->com->mypackage->myfile.java
From myfile.java I need to read the directory "MyDirectory" in webapp. As new File("path of the directory")
But I don't know how to specify the path of the directory here.
Try
ServletContext context = getServletContext();
InputStream is = context.getResourceAsStream("/MyDirectory/FileInThatDir.txt");
alternatively use getResource() instead of getResourceAsStream()
At runtime, there is no maven anymore (thank God!), and there is no directory anymore. All you have is a war file, and everything is either in the war file, or somewhere outside of the app, on the file system.
Use ServletContext.getResourceAsStream() to load a file from the webapp's context.
You haven't mentioned what technology/framework you use, but I suppose you're using at least Java servlets, because you mentioned it's a web application. So you can use ServletContext.getRealPath() to get the path in the filesystem:
String fullPath = getServletContext().getRealPath("/MyDirectory");
System.out.printf("real filesystem path: %s", fullPath);
Update:
Please note that JavaDoc of the method says:
The real path returned will be in a form appropriate to the computer and operating system on which the servlet container is running, including the proper path separators.
Resources inside the /META-INF/resources directories of JAR files bundled in the application's /WEB-INF/lib directory must be considered only if the container has unpacked them from their containing JAR file, in which case the path to the unpacked location must be returned.
This method returns null if the servlet container is unable to translate the given virtual path to a real path.
This means that the returned path:
is not portable (e.g. in Windows with \, in Linux/macOS with /, etc).
might be null, e.g. if the resource is virtually in memory, in a JAR file, or so.
Therefore: please follow my answer only with those caveats in mind, and better follow the other answers using ServletContext.getResourceAsStream()
I couldn't access the servlet context since the service where i need to read data from the webapp folder is not aware of the current servlet context.
Anyone who is using Spring can never the less access the webapp folder by simply implementing the ResourceLoaderAware Interface provided by the spring framework.
public class SomeService implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
#Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public readFromWebappFolder() {
Resource resource = resourceLoader.getResource("path/to/file/in/webappFolder");
}
}
I would like to create an xml file and store in a folder within my spring Mvc web application.
I can get the root of my application with request.getContextPath()
but
how do i get the application's relative path so it will work on any machine indipendently by the location of the application's folder?
Like C:/folder/folder/MYAPPLICATIONROOTFOLDER
You want to do this.
First, you need to get the ServletContext. I don't know how this is done in Spring MVC, but it's there somewhere.
Then you can do:
ServletContext ctx = getServletContextFromSpringSomehow();
String path = ctx.getRealPath("/folder/filename.txt");
FileWriter fw = new FileWriter(path);
The key here is ServletContext.getRealPath. It gives you the local file system path of a resource from within your webapp. Observer that you use "/" here, as it's a URL, not a file name. The container will give you a valid file name in return. Note, this only works if your container explodes your WAR, or you deploy an exploded WAR. If the WAR is NOT exploded, you will get a null back from the container.
Also note, this WILL work for non-existent files. The container does not check for the actual existence of the file. But it will be up to you to actually create any missing intermediate directories, etc.
Finally, of course, that even if you get a file path back, doesn't mean you can actually write to that path. That's a OS permission issue outside of the scope of the container.
One solution is to bundle the XML with the clases in the JAR/WAR and then use the getResourceAsStream() to leverage the ClassLoader to locate the file.
If I put the file foo.xml with the classes in com/stackoverflow/example, I could then locate the resources from objects in that bundle with
InputStream is = MyClass.getResourceAsStream( "com/stackoverflow/example" );
and from here process the file with a XML parser or whatever else you wanted to do to read the file.