I'm creating a dynamic web project in Eclipse where I frequently have write and read to and from an XML file. The file is in my project workspace in a folder called xml. I was wondering if Java provided some way to access the file without hard coding the file path. I've been looking around for a while for a solution but I haven't really founding anything that's really clear. Thanks!
You could just drop it in the classpath as suggested by others, but you won't be able to write to it.
Rather supply the absolute path as a VM argument or environment variable so that you don't need to hardcode it.
E.g.
-Dconfig.location=/path/to/config/file
with
File xmlFile = new File(System.getProperty("config.location"), "some.xml");
// ...
As a completely different alternative, you could consider a database.
You can get the proper path using the following from your Servlet:
String filename = getServletContext().getRealPath("/xml/config.xml");
NOTE:
getRealPath may return null if the file is inside a WAR file. In that case, if your file is in WEB_INF/classes, then you could use ServletContext.getResourceAsStream("/config.xml").
See this link:
I don't think the Servlet API gives you anything that would result in a reliable, writable, path to put work files in all containers. If your container runs the WebApp right out of the WAR, getRealPath() couldn't possibly point to something you can actually write to. I think that your only option here that is supported regardless of container is to hard code some path in the web.xml. Do it as a Context Parameter and you may be able to change it at deployment time. At the end of the day, you must declare a fully qualified path in either code or configuration to get the effect you seek.
Alternatively, do you really need to know the name of the file? In some Servlet apps I've managed to get the effect of dynamically writable storage through plain-jane java.io.file.createTempFile: http://docs.oracle.com/javase/1.4.2/docs/api/java/io/File.html#createTempFile(java.lang.String, java.lang.String)
Related
I have a java multiple modules sbt project and some of them contains a resources folder.
module-1
resources
template.xls
module-2
resources
other.xls
After packaging I got :
lib/module-1.jar
lib/module-2.jar
And I run my program like any other java application : java -cp "lib/*" MainClass.
My problem is accessing the template.xls from module-2.jar.
At first, I've tried the lines below to get my template :
URI template = getClass().getResource("/template.xls").toURI();
Files.newInputStream(Paths.get(template), StandardOpenOption.READ);
In development mode it works. But not on the server (after deployment), it cannot find the resource.
java.nio.file.FileSystemNotFoundException: null
[jar:file:/.../lib/module-1.jar!/template.xls]
After some research I modified my accessing code like the following to get it works in both modes (development and deployed) :
InputStream templateIS = getClass().getResourceAsStream("/template.xls");
I cannot understand why !
What is the difference between the two methods ?
Files.newInputStream, as the name suggests, can open files. It cannot open anything else. It's just for files.
The concept of an InputStream is much more abstract. When you open a file for reading, you get an inputstream, yup. But you also get inputstreams for many other things: Reading from a network connection; reading the unpacked contents of zip files. Reading the output of a decryption operation. The name says it all really: It's input, and it's a stream of data. Which applies to so much more than 'file on a filesystem'.
Paths.get(template) produces a path object, which represents a file on the file system. If template is derived from a URI, this does not work unless the URI that you have so happens to be a URI to a file object; most URIs are not to file objects.
Putting it all together, in your first sample, you find a resource on the classpath (which can be files, but don't have to be. For example, they could be entries in a jar file), you then ask its URI, feed it to the Paths API to turn it into a Path object, and then ask the Files API to turn this into an InputStream, which only works if the URI represents a file.
In the second snippet, you just ask the classloader system to get you an inputstream. It knows how to do that (after all, java has to load those class files!). If the resource you're asking for so happens to be represented by a file, it's going to do, internally, more or less the same thing as your first snippet: Use the Files API to open the file for reading. But if it's anything else, it knows how to do that too – it also knows how to get resources across a network, from inside jar files, generated on-the-fly – the concept of class loading (which is what class.getResource lets you access) is abstracted away.
NB: You're using it wrong. The proper way is ClassYouAreWritingIn.class.getResource and ClassYouAreWritingIn.class.getResourceAsStream; getClass().getResource is not correct; that breaks when subclassing, whereas the correct form doesn't.
From docs,
The method getResource() returns a URL for the resource. The URL (and
its representation) is specific to the implementation and the JVM
(that is, the URL obtained in one runtime instance may not work in
another). Its protocol is usually specific to the ClassLoader loading
the resource. If the resource does not exist or is not visible due to
security considerations, the methods return null.
If the client code wants to read the contents of the resource as an
InputStream, it can apply the openStream() method on the URL. This is
common enough to justify adding getResourceAsStream() to Class and
ClassLoader. getResourceAsStream() the same as calling
getResource().openStream(), except that getResourceAsStream() catches
IO exceptions returns a null InputStream.
So, getResourceAsStream() the same as calling getResource().openStream(), except that getResourceAsStream() catches IO exceptions returns a null InputStream.
Your first scenario doesn't work because the template.xls is packaged within a jar file. It's not a file on the filesystem (whereas it likely is in your development env prior to packaging as a file). As such the Files API can't find it.
Class.getResourceAsStream() makes use of the classloading mechanism, and the class (obviously) is loaded from the .jar file.
I'm currently writing a replacement shell in java for windows, and my application is currently creating a config file using
File userSettings = new File("Gyroscope.properties");
However, when this is run by windows, the current directory is set to C:\windows\System32\ and the program doesn't have write permissions to this directory.
What would be a better location for the configuration file to be written to? It would be preferable to have it relative to the jar file, however I do not know a good method of doing so, and other stack overflow questions have had unclean solutions to this exact problem, so I am looking for other locations as options.
The configuration is machine specific, and should be common to all users.
Use the Preferences class, it'll make sure that the settings are written to somewhere where it's allowed (such as user's home dir or the registry).
I will describe a possible solution. There should be several way to achieve it.
Put your config file next to your jar. Put explicitly the config file in your classpath while invoking java (e.g. java -cp .../Gyroscope.jar;.../Gyroscope.properties <your_main_class>).
Now you can access your properties file as a ressource (ClassLoader.getSystemResourceAsStream("/Gyroscope.properties"). Use whatever classloader you want as soon as it is convenient and cleam for your actual implementation. Once your have an inputstream, you can load your properties into a Properties object as usual.
Hope this will help.
Would like to build the absolutepath use this information. Anyone knows how?
If you know the name of the resource within the class path, i believe you can call
URL j = loader.getResource(name);
and from the URL you can tell where it is either in the file system, or in what jar it resides.
Judging by your other question, you are going about this wrong. If you only need to read from the resource, use getClass().getResource() to locate the resource and use it from the URL.
If OTOH the app. might also change the information in the resource, it is best to store a copy of the changed resource on the local file system. Don't store it in the 'user.dir' - which is fragile and unreliable, but a sub-directory (e.g. based on the reverse domain name) of 'user.home'.
As i know it returns the application path? But what exactly the use of it.
In many environments the application user is not allowed to read any files outside of the deployment directory. This is mostly done for security purposes - for example if someone hacks your application they won't be able to read a passwords file.
And in professionally managed environments developers often don't have a say in which directory the application will be placed.
So if you need to read a file like properties, images, certificates, etc. you can place it in the application directory (or .war file) and use getRealPath("") to get the path you need to load.
As an alternative you can place the external files on the classpath but there are sometimes issues with this. For large files most app servers will try to load the entire file into memory and cache it if it is on the classpath.
The getRealPath() gives the absolute path (on the file system) leading to a file specified in the parameters of the call. It returns the path in the format specific to the OS.
The getContextPath() on the other hand returns the URI or the relative path to the resource.
As far as I remember, I've used it to save images or other data files, since it allows you to see where your application is deployed at the moment. For example, Eclipse and Tomcat will create a temporary folder that's buried deep somewhere within your Eclipse profile and deploy the app there.
This is a real path in file system.
From javadoc:
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. This method returns null if the servlet container cannot translate the virtual path to a real path for any reason (such as when the content is being made available from a .war archive).
I think it is very clear. Why do we need this? Sometimes web applications perform some manipulation in file system. For example read stuff from files, write files etc. This API allows you to access the place where your JSPs and other stuff is really stored.
I am doing a project in java and in that i need to add and modify my
text file at runtime,which is grouped in the jar.
I am using class.getResourceAsStream(filename) this method we
can read that file from class path.
i want to write into the same textfile.
What is the possible solution for this.
If i can't update the text file in jar what other solution is there?
Appreciate any help.
The easiest solution here is to not put the file in the jar. It sounds like you are putting files in your jar so that your user only needs to worry about one file that contains everything related to that program. This is an artificial constraint and just add headaches.
There is a simple solution that still allows you to distribute just the jar file. At start up, attempt to read the file from the file system. If you don't find it, use default values that are encoded in you program. Then when changes are made, you can write it to the file system.
In general, you can't update a file that you located using getResourceAsStream. It might be a file in a JAR/ZIP file ... and writing it would entail rewriting the entire JAR file. It might be a remote file served up by a Url classloader.
For your sanity (and good practice), you should not attempt to update files that you access via the classpath. If you need to, read the file out of the JAR file (or whatever), copy it into the regular file system, and then update the copy.
I'm not saying that it is impossible to do this in all cases. Indeed, in most normal cases you can do it with some effort. However, this is not supported, and there are no standard APIs for doing this.
Furthermore, attempts to update resources are liable to cause anomalies in the classloader. For example, I'd expect resources in JAR files to not update (from the perspective of the application) until the application restarted. But resources in exploded JAR files probably would update ... though new resources might not show up.
Finally, there are cases where updating a resource is impossible:
When the user doesn't have write access to the application's installation directory. This is typical for a properly administered UNIX / Linux machine.
When the JAR file is fetched from a remote server, you are likely not to be able to write the updates back.
When you are using an arbitrary custom classloader, you've got no way of knowing where the actual bytes of an updated resource should be stored, and no way of storing them.
All JAR rewriting techniques in Java look similar. Open the Jar file, read all of it's contents, and write a new Jar file containing the unmodified contents (and the modifications you whished to make). Such techniques are not advisable for a Jar file on the class path, much less a Jar file you're running from.
If you decide you must do it this way, Java World has a few articles:
Modifying Archives, Part 1
Modifying Archives, Part 2
A good solution that avoids the need to put your items into a Jar file is to read (if present) a properties file out of a hidden subdirectory in the user's home directory. The logic looks a bit like this:
if (the hidden directory named after my application doesn't exist) {
makeTheHiddenDirectory();
writeTheDefaultPropertiesFile();
}
Properties appProps = new Properties();
appProps.load(new FileInputStream(fileInHiddenDir));
...
... After the appProps have changed ...
...
appProps.store(new FileOutputStream(fileInHiddenDir), "Do not modify this file");
Look to java.util.Properties, and keep in mind that they have two different load and store formats (key = value based and XML based). Pick the one that suits you best.
If i can't update the text file in jar what other solution is there?
Store the information in any of:
Cookies
The server
Deploy the applet using 1.6.0_10+, launch it using JWS and use the PersistenceService to store the information. Here is my demo. of the PersistenceService.
Also, if your users will agree to a trusted applet (which seems overkill for this), you might write the information to a sub-directory of user.home.