Java - Relative path of a file in a java web application - java

I want to read a file from a java web application. I don't want to give the absolute path of the file. I just want to put the file in some directory of my web application.
Or
It can be placed along with .war file (packaged web application).
What relative path to give for the file. I tried ./filename.csv but it didn't work.
========Updated========
I will deliver a WAR file (packaged web application) to my client. This web application will read a file (lets say SuppliedFile.csv) which will be copied to the server by the client. So I need a mechanism (that will work irrespective of whether the application server will unpak the WAR or not) so that web application can read that file.
Note:
I am not using the SuppliedFile.csv in a servlet... I am using it in a plain Java class...

Do you really need to load it from a file? If you place it along your classes (in WEB-INF/classes) you can get an InputStream to it using the class loader:
InputStream csv =
SomeClassInTheSamePackage.class.getResourceAsStream("filename.csv");

You may be able to simply access a pre-arranged file path on the system. This is preferable since files added to the webapp directory might be lost or the webapp may not be unpacked depending on system configuration.
In our server, we define a system property set in the App Server's JVM which points to the "home directory" for our app's external data. Of course this requires modification of the App Server's configuration (-DAPP_HOME=... added to JVM_OPTS at startup), we do it mainly to ease testing of code run outside the context of an App Server.
You could just as easily retrieve a path from the servlet config:
<web-app>
<context-param>
<param-name>MyAppHome</param-name>
<param-value>/usr/share/myapp</param-value>
</context-param>
...
</web-app>
Then retrieve this path and use it as the base path to read the file supplied by the client.
public class MyAppConfig implements ServletContextListener {
// NOTE: static references are not a great idea, shown here for simplicity
static File appHome;
static File customerDataFile;
public void contextInitialized(ServletContextEvent e) {
appHome = new File(e.getServletContext().getInitParameter("MyAppHome"));
File customerDataFile = new File(appHome, "SuppliedFile.csv");
}
}
class DataProcessor {
public void processData() {
File dataFile = MyAppConfig.customerDataFile;
// ...
}
}
As I mentioned the most likely problem you'll encounter is security restrictions. Nothing guarantees webapps can ready any files above their webapp root. But there are generally simple methods for granting exceptions for specific paths to specific webapps.
Regardless of the code in which you then need to access this file, since you are running within a web application you are guaranteed this is initialized first, and can stash it's value somewhere convenient for the rest of your code to refer to, as in my example or better yet, just simply pass the path as a paramete to the code which needs it.

If you have a path for that file in the web server, you can get the real path in the server's file system using ServletContext.getRealPath(). Note that it is not guaranteed to work in every container (as a container is not required to unpack the WAR file and store the content in the file system - most do though). And I guess it won't work with files in /WEB-INF, as they don't have a virtual path.
The alternative would be to use ServletContext.getResource() which returns a URI. This URI may be a 'file:' URL, but there's no guarantee for that.

Many popular Java webapps, including Jenkins and Nexus, use this mechanism:
Optionally, check a servlet context-param / init-param. This allows configuring multiple webapp instances per servlet container, using context.xml which can be done by modifying the WAR or by changing server settings (in case of Tomcat).
Check an environment variable (using System.getenv), if it is set, then use that folder as your application data folder. e.g. Jenkins uses JENKINS_HOME and Nexus uses PLEXUS_NEXUS_WORK. This allows flexible configuration without any changes to WAR.
Otherwise, use a subfolder inside user's home folder, e.g. $HOME/.yourapp. In Java code this will be:
final File appFolder = new File(System.getProperty("user.home"), ".yourapp");

The alternative would be to use ServletContext.getResource() which returns a URI. This URI
may be a 'file:' URL, but there's no guarantee for that.
You don't need it to be a file:... URL. You just need it to be a URL that your JVM can
read--and it will be.

there is another way,
if you are using a container like Tomcat :
String textPath = "http://localhost:8080/NameOfWebapp/resources/images/file.txt";

Related

How to reference a file in the source dir from any Java code?

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.

System.getProperty("catalina.base") There can be scenario where client may use any other server

I am reading a properties file from the Tomcat\conf\somename.properties directory using
String demo = System.getProperty("catalina.base") +
File.separator + "conf" + File.separator + "somename.properties";
This is working perfectly fine with Tomcat. But, there can be scenario where client may use any other server like Glassfish or Websphere, in that case I won't be able to get System.getProperty("catalina.base").
How should I solve it properly? I'm able to do that using ResourceBundle but for that I have to keep my properties file in my build, which I don't want. I just want to read my properties file from outside my build.
There are basically two ways.
Just add its path to the runtime classpath so that you can get it from the classpath the usual way. In case of Tomcat, you can add external folders to the runtime classpath by specifying it in the shared.loader property of /conf/catalina.properties. E.g.
shared.loader = ${catalina.home}/conf
Or better, don't be server-specific
shared.loader = /path/to/folder
Other servers also supports adding external folders to the classpath, consult their documentation.
This way you'll be able to get an InputStream of it from the classpath as follows:
InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("/config.properties");
Properties properties = new Properties();
properties.load(input);
// ...
Add another server-independent system property yourself, you can set as a VM argument.
-Dconfig.location=/path/to/folder
In case of Tomcat, you can set it as JAVA_OPTS environment variable, or edit the catalina.bat startup file or edit the Windows Service settings (when it's installed as Windows Service), etc. Other servers supports similar constructs as well.
This way you can obtain it as follows
File file = new File(System.getProperty("config.location"), "config.properties");
InputStream input = new FileInputStream(file);
Properties properties = new Properties();
properties.load(input);
// ...
Either way you choose, when distributing your application, you should document it properly so that the serveradmin can configure it accordingly.
Unrelated to the problem, the ResourceBundle is not the right way to read configuration properties files. It's intented for localized content for internationalization.
First use the ServletContext.getServerInfo() to determine the container. Then based on the container, use container specific ways of getting the information. For e.g. if the method returns "tomcat*" then you can use catalina.base thing, if it returns glassfish, then use some glassfish specific ways, and so on.
Simply put, don't rely on catalina.base, that is your first problem :)
Or, more precisely, the java servlet api gives you access to resources inside your web application, so your app is truly portable (not only between servers, but also you can put it anywhere on the file system, zipped as a war or exploded)
Say you put your file at <webapp-root>/WEB-INF/somename.properties, then this is what you do in your servlet, listener, or other web-aware classes:
getServletContext().getResourceAsStream("/WEB-INF/somename.properties");
See similar question here.
Another alternative is to use regular java api to search for files in your classpath, e.g. this.getClass().getResource("/somename.properties"). In the case of a web application, this will find such a file located under /WEB-INF/class/ or any jar under /WEB-INF/lib/.
Finally, if you can't put the file inside your web application, you can put it anywhere on the hard drive and use some config param (e.g. a system property, or a context parameter in web.xml) to refer to it.

How to load resources in Tomcat via Java File api

I have to use a library that accepts path to a directory and loads it via File(path);. What should be the path in tomcat webapp context ?
I always work with classpaths, but this API is really not thought through...
I don't have experience with java execution context in tomcat, but it seems to me a bad idea doing something like this
System.getProperty("catalina.base") + "webapps/app/WEB-INF/classes/profiles"
Does tomcat provides java execution (AKA $PWD) path for resources in application context ?
Use ServletContext#getRealPath() to convert a webapp-relative path to an absolute disk file system path. Given your example, the following should do:
String path = getServletContext().getRealPath("/WEB-INF/classes/profiles");
File file = new File(path);
// ...
Note that this requires that the WAR is expanded, otherwise it will return null. Tomcat by default expands the WAR, but some other servletcontainers/configs don't. Keep this in mind with regard to portability.
Application resources do NOT necessary exist on file system, therefore the search from context path approach is simply wrong.
You should provide an external configuration to your application (a property file or DB), where you should define the path depending on environment.
Suggested order of searching for configuration:
System.properties: -Dmy.configuration=/home/tomcat/my.properties
System environment: MY_CONFIGURATION=/home/tomcat/my.properties
Default value: ./my.properties (for Tomcat it is usually bin folder, but not guaranteed)
Fail and complain.

Problem with getResourceAsStream

I am writing an upload and a download function, and I try to have this two methods to write to or read from the same folder, and I ran into some problem with getResourceAsStream. (The software is run on glassfish)
upload: The method upload to this folder: /home/phamtn8/glassfishv3/glassfish/domains/domain1/applications/Documents/Documents-war_war/drawings/Liandro.jpg --> work great
download: stream = the above path
input = this.getClass().getResourceAsStream(stream); //This return null
The location of the class files that contain these upload and download methods is at:
/home/phamtn8/glassfishv3/glassfish/domains/domain1/applications/Documents/Documents-war_war/WEB-INF/classes/org/xdrawing/web. If I put the jpg file here, the getResourceAsStream work.
NOTE: this.getClass.getName() return org.xdrawing.web.FileName
Please help !!!
getResourceAsStream(..) treats paths from the root of the classpath. And yours seems to be the root of the machine. So use new FileInpuStream(fullPath) instead.
In fact, there is another getResourceAsStream method that belongs to the ServletContext. It treats paths from the root of the web application, and is more suitable for web applications. (your web app root is Documents-war_war/)
But the best practice with file uploads and downloads is to store them in a totally different location from your web application - say /home/appname/uploads, so that you can deploy and undeploy the web-app without losing any data. You will just need a configuration option (a <context-param> in web.xml for example) that points to the absolute location of the uploads, and use the FileInputStream approach (or OutputStream respectively)

How can I create a new folder in my web-app (for upload photo album) with Grails on Tomcat?

i want to create a photo album.
But I want to organize on the server the albums so I want to create new folders :
/myapp
/myapp/albums
/myapp/albums/1
/myapp/albums/2
...
How can I do that on tomcat with Grails ? It create all new folder in tomcat/bin not in tomcat/webapps/myapp/
When I had to do something similar I defined a root path in my Config.groovy like
environments {
production {
rootPath="/home/user/reports"
}
development {
rootPath="c:\\reports"
}
test {
rootPath="c:\\reports"
}
Then create a directory like the following.
import org.codehaus.groovy.grails.commons.ConfigurationHolder as Conf
tempFile=new File(Conf.config.rootPath+"dirName")
tempFile.mkdir()
I don't do Grails, but since it runs on top of the Servlet API, you may find this answer (a steering in the right direction) useful as well.
First, get a handle of the ServletContext. Normally you would use the GenericServlet-inherited getServletContext() method for this. Then make use of the ServletContext#getRealPath() method to convert a relative web path to an absolute local disk file system path (because that's the only which java.io.File reliably understands).
String absolutePath = getServletContext().getRealPath("albums/1");
File file = new File (absolutePath);
// ...
If you use relative paths in java.io.File stuff, then it will become relative to the current working directory which depends on the way how you startup the server and which indeed may be Tomcat/bin as you experienced yourself.
That said, there's another major problem with this approach: if you create folders in an exploded webapp, they will get lost whenever you redeploy the webapp or even restart the server! Rather create folders outside the webapp's context, in a fixed path somewhere else at the disk file system. Or if you want better portability (but poorer metadata information), then consider storing it in a database instead.

Categories