I am trying to configure a Resource in "context.xml" to enable file sharing (viewing and download) from outside the web application. So basically what I would need in order to be compliant, would be to define that resource to point to a sort of "ExternalContext" (a custom one) from where I would get the files as streams, exactly as I normally do when accessing existing resources inside the web app.
I haven't found code pieces to clue me out. I am using apache tomcat 6.0 but in the documentation is not very clearly stated how to do that.
What happens in Tomcat when you specify a URL that cannot be mapped to any servlet or JSP is that the URL is sent to Tomcat's internal default servlet.
The default servlet examines the incoming URL, removes the server and context parts off the front, strips off the trailing query string (if any) from the end, and interprets the remainder as a resource pathname. This pathname is then essentially used to invoke the getResourceAsStream() request method which then feeds into a read/write loop that copies the resource verbatim to the HttpResponse output stream. This is totally unlike a file server, in that everything is done via HTTP. You cannot use standard filesystem open/close/read/write methods on the client to obtain access to the server-side resources.
The J2EE getResource and getResourceAsStream methods take as their input an absolute resource reference. That's "absolute" relative to the root of the application's WAR, which technically is supposed to be a ZIP file, although Tomcat, like many J2EE servers permits what is known as an "exploded WAR", which is simply a WAR unzipped into its own personal directory.
I'm using Tomcat as an example, but for all intents and purposes, any J2EE-compliant webapp server is going to behave like this.
So to serve files from outside the WAR, you have to basically reproduce the behavior of the Tomcat default servlet. Meaning you need to create a servlet that will decode an incoming URL into something that can be used to locate a file, followed by code to copy the contents of that file to the HTTP output stream.
An IDE is no substitute for an Intelligent Developer.
Joined: Apr 21, 2010
Hi Tim, thanks for your insisghts.
I preffer to deal with the "folder navigation" inside the backing bean and open the files in a JavaSE fashion, by using absolute paths in the server, instead of writing any relative paths in the url. However I am thinking of security considerations to bear in mind, and besides that, I don't know what would make the difference regarding security between this approach and programming a custom servlet to handle those requests, then anyway, this custom servlet would access the resources as well with absolute paths in the end.
Carlos Conti wrote:Hi Tim, thanks for your insisghts.
I preffer to deal with the "folder navigation" inside the backing bean and open the files in a JavaSE fashion, by using absolute paths in the server, instead of writing any relative paths in the url.
I totally don't understand what this means. You make it sound like JSF beans have some sort of file browsing capability. Unless maybe you're thinking that you can put the copy logic in JSF backing beans.
You can't. JSF will add a lot of HTML-related freight that will make garbage out of any image files, PDF's, and other non-HTML files. You need to do the file copy in a non-JSF HTTP request handler, such as a servlet. Usually file-copy servlets aren't very complicated, however.
Also, the URL is not a filename path. It just looks like one. The default servlet simply takes advantage of that to make it easier to construct an actual filename path from the parsed URL.
When opening a file in a webapp, it's always a good idea to use an absolute path. In a webapp you never can tell what the current directory may be, and the current directory is the basis for relative paths (relative to the current directory, to be precise!). However, since, like I said, a URL is not a filename path, the file-copy mechanism is totally at liberty to pick whatever absolute basis it wants to. Though picking the root of the file system isn't a good choice, since most OS's have directories full of stuff that are best not made public.
As far as configuring in an absolute base directory, you can hard-code it, put it in a properties file/database record, or use a mechanism such as JNDI. I prefer the JNDI route, since most of the other solutions tend to require keeping separate editions of the app for development, test, and production. JNDI allows you to supply the value as a deployment descriptor (Context) setting.
Joined: Apr 21, 2010
hi Tim, thanks again.
What i do is much simpler to all these proposals, however one of them is the one (JNDI) I would like to finally implement, since what I have implemented so far is not a long-term solution.
My implementation so far consists of a page holding one link and two datatables. The link points to the parent folder relative to the currently selected path, the first datatable holds folder names and the other datatable file names, all as you may guess, representing the full contents of the current path,which is in fact a physical path "outside the web app context". That current path is hold in the backing bean.
The link enables navigating upwards in the folder hierarchy, whereas the folder links allow to deepen in the hierarchy. File names point to their correspondent file reference to enable viewing/downloading.
in the backing-bean the current path is updated when triggering the "navigation" actions included in the link, and the folder datatable. When clicking on them I basically alter this path string field in the backing-bean and refresh the page, once the lists holding folder and file names to feed the datatables are updated.
the methods responsible for feeding the data in the lists, are the ones which read the filesystem in a J2SE fashion to get the data.
I wasn't meaning to copy or presumed any navigation capability, since in a very simple fashion, i build it in. I presume the mechanism could be more or less similar if implemented a JNDI route, however I must admit I have no clue how to do that, and no matter how long I search the internet for examples, I won't find any. I have checked the JNDI documentation, but its pretty extensive and moreover it only talks about LDAP directories but not filesystems or how a physical path to a base directory can be enabled.
Custom servlets do not appear to me to be the best solution either, since i don't want to reflect any physical structure, absolute or relatively in the URL string.
Hope I could make all the points clear, and nothing is left to confusion or ambiguity. Could you please provide any example on how should this JNDI resource or link could look like? how must it be defined in context.xml?
I don't mean to imply use of JNDI to obtain actual file data. I meant use JDNI as a directory lookup to obtain the base directory path for the files you wish to copy.
JSF is fine for the navigation, but the actual getting the data from server to client CANNOT be done via JSF or J2SE. It has to be done via J2EE. The only J2SE part is in the code where the server itself opens and reads the data. At that point, it MUST place the data in the J2EE output response stream if it intends the data to be made available to the client.
Servlets are invoked via URLs, based on the URL mapping in web.xml. The URL format can be entirely arbitrary as long as it is syntactically correct, and in no way, shape, or form does it reflect the physical structure of the WAR or anything else except as you make it so.
Joined: Apr 21, 2010
thanks again. The getting the data in the client is done through a custom Resource class which produces the correspondent ByteArrayResource which is made available via iceutputResource. Which means it is already handled by the resource servlet provided in IceFaces... I guess (I am still not that far with JSF you know...)
So here JNDI would help in the lookup of the files isnt'it? In the same way a filter would do, although across a full folder subset. That's interesting but I think it is not the best solution for this particular problem.
Should I implement a lookup function, then it would be the appropiate solution, but for folder navigation given a base path, I think this approach is fine.