How do you read a .jks file from a rest web application deployed with Tomcat server ? I am using FileInputStream to read the file. I am currently having difficulty dealing with the relative paths used to read a file in a web application. I know the file will be under WEB-INF/classes. What will be the best way to read the file using FileInputStream. I know using classLoader. but then it returns the InputStream.
It is a bad practice to use direct file access (FileInputStream) for reading resources that are bundled with the application; the reason is that it leads to precisely the sort of problems like you're experiencing. In a managed runtime environment, the developer has very little control over the paths where resources are actually located.
For servlet-based applications (which you are using, as you mentioned that you're using Tomcat), the best way to go about it is to use the context classloader:
Thread.currentThread().getContextClassLoader()
and load the resource from there, getting an InputStream. In other words - you're doing things correctly at the moment. Don't use FileInputStream. Unless you have an absolutely good reason for doing so, in which case I'm going to be extremely surprised.
The best way to access files which are not bundled with EAR / WAR is to use java.util.Properties. I am sure that files like jks files are never bundled with EAR , they are in domain folder of the server or server dir ( weblogic , websphere.). Properties file should be placed on file location independent of the server installed directory.
Here is the solution I can think of :
1. Initialize a Servlet with init-param in web.xml
2. The init-param can be the absolute file location
<param-name>porperies-file</param-name>
<param-value>/usr/properties/abc.jks</param-value>
3. In the static block of the Servlet , load the properties file / jks
file and store it in parameter
This look complex but many frameworks give the option of locating files like ResourceBundle files through Absoulute path. Check the documentation for Spring's FileSystemResourceLoader.
The advantage of such a process is if the "jks" file changes you just need to start the server. In the case where you have the propeties file in EAR , you need to deploy the application and then restart the server. Consider this also , you can have logic to check the freshness of the file. In this case the system need not be re-started at all but you have to put extra bit of logic of synchronization.
if you make a KeyStore(w/o password,and defaultType)
InputStream is = this.getClass().getClassLoader().getResourceAsStream("jskFileName");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(is, null);
but FileInputStream is really good if you...
Please note that when you use the cluster servers.
for example
/contextPath
/WEB-INF
/pathA/
/sample.jks
ServletContext sc
String path="/pathA/sample.jks";
String realPath = sc.getRealPath(path);
FileInputStream fis = new FileInputStream(new File(realPath));
Related
Environment: Ubuntu 18 + Spring boot 2.0 + JDK 11
It performs correctly when the application runs in IntelliJ, but it doesn't when reading the file after Spring boot maven plugin packaging file.
PS: The file indeed could be found in the packaged jar file!
java.io.FileNotFoundException: class path resource [jmxremote.password]
cannot be resolved to absolute file path because it does not reside in the
file system: jar:file:/home/XXX/YYY/target/YYY-1.0-Final.jar!/BOOT-
INF/classes!/jmxremote.password
You are most likely attempting to use java.io classes to load a class path resource. This won't work, the java.io classes are designed only for file system operations.
Either extract jmxremote.password outside of the JAR or use the regular InputStream to access it as a class path resource e.g. by using Class.getResourceAsStream():
try (InputStream in : getClass().getResourceAsStream("/jmxremote.password")) {
// read input
}
TL;DR
In the Spring Framework environment focus on using Spring utility tooling for handling resources (like the ResourceUtils class) which nicely encapsulate lower-level, operating system dependent IO operations. The ResourceUtils already contains multiple traps to figure out if the project you are running is exploded (run in IDE) or packaged (inside a JAR file).
The answer provided by Karol seems the easiest and is relatively bullet-proof, until you need a certain level of flexibility to specify file location (inside a jar file but with the possibility to define it externally and provide somewhere in the file system). Then the approach with getResourceAsStream() method won't work.
Standard Java IO (java.nio) is using FileSystemProvider classes to delegate IO operations (like creating, reading and deleting files).
A provider is identified by a URI scheme. The default provider is identified by the URI scheme "file." It creates the FileSystem that provides access to the file systems accessible to the Java virtual machine. The FileSystems class defines how file system providers are located and loaded.
So, if your file is somewhere on the filesystem, there are no issues, and everything works fine. Technically, the URL returned by Application.class.getResource("").toURI() starts with file:// and contains a valid filesystem path.
Having said that, when your file "land" inside a jar file, the Application.class.getResource("").toURI() returns something more like file://{jar-location}!/ (mind the exclamation mark), which is not a valid file schema path and Java doesn't know how to handle it. An additional file system provider needs registering.
FileSystems.newFileSystem(uri, emptyMap());
Java figures out (based on URI) the scheme and registers a new file system. From now on, standard java.nio file operations can be used.
For example, if you have some files in a /webapp folder which can (but don't need to) be inside a jar file and you'd like to list them.
// Load zip specific filesystem provider when run from inside a fat-jar
URI uri = Application.class.getResource("").toURI();
if (uri.toString().contains("!")) {
FileSystems.newFileSystem(uri, emptyMap());
}
URI rootFolder = Application.class.getResource("/webapp").toURI();
List<Path> banners = Files.list(Paths.get(rootFolder))
.collect(Collectors.toList());
Random rand = new Random();
Path path = banners.get(rand.nextInt(banners.size()));
log.info("Random image: {}", path.getFileName());
byte[] bytes = Files.readAllBytes(path);
Installation of a new file system provider is global and should be done only once.
I am working on a SpringMVC project which runs a number of automated tests on a database. The access details for this database are located in a .properties file. This file is located within the project directory.
FileInputStream fis = new FileInputStream("batch-dm.properties");
propFile = new Properties();
propFile.load(fis);
As the file is stored in the project directory the FileInputStream should be able to access it no?
If I provide the absolute path e.g.
FileInputStream fis = new FileInputStream("C:/workspace/Project/batch-dm.properties");
It recognises the file and runs properly.
Do I need to sore this file in a different location because it is a Spring MVC project?
Thanks
Just to clear out your mind, try to see what is the value that outputs System.getProperty("user.dir") (let's call it A), this will print the complete absolute path from where your application was initialized, more specifically it will print the directory from where the JVM was started.
If you doesn't supply a parent path to the file that you are trying to open, the (A) path is taken by default and the file is looked inside that directory. So please, have in mind that.
Aditional information
If you absolutely need that file you should include it in your project so you can access it as a resource. Resource is a file that is included in your project and came bundled with the generated .jar or .war for re distribution.
My advice is to put the file in the package and use as a resource as it the safer way to work with external resources that should be shipped with your project.
Take a deeper look at this post for more about practical way of handling resources.
Please refer below link:
https://stackoverflow.com/a/2308388/1358551
You may have to use getResourceAsStream().
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.
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)
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.