Spring load a file - java

I need to load a file from within a directory in the root of a WAR using Spring
This is the directory structure
rootOfWar
--static-dir
---- my-file.css
--WEB-INF
---- classes
.....
It is a normal WAR.
In a Spring #RestController I need to be able to read and write to my-file.css file. What is the best way to get the File, ServletContextResource or?
More Details
- The location of the file is out of my control I cannot move the file.
- The jee container is Tomcat.
- The Spring version is current 4.1.6
- The Spring environment is not using XML only annotations with WebApplicationInitializer, WebMvcConfigurerAdapter and an annotation configuration class.
Is there another way to do this like specify a file as a resource in the configuration so that it is loaded by the frame work and accessible within the application?
Basically I have JEE knowledge but my Spring knowledge on best practices concerning read/write are lacking.

If you need to modify a file you should not make it part of the WAR. Store it somewhere outside the web package and reference it there. Everything else will lead to problems, especially when you deploy to Websphere which is often run in a restricted environment where writes are rejected.
But I consider overwriting files in the web path bad design, because you are likely to run into caching issues. Better write a servlet that generates the CSS you need. If you would be able to name the content that should overwrite your css file, you are also able to render this dynamically.
Something like this may be already sufficient:
#RequestMapping(value = "/my.css", produces = "text/css;charset=UTF-8")
public String showCss() {
return "<here goes your css>";
}
(This is written from my memory and not tested).
The good thing is that you can modify the css any time you want and even set caching information as needed.

Related

Accessing external files on network directories when deployed with microservice Jar file in Spring Boot

So I have a microservice app that does image processing with ImageJ which I have created a microservice using spring boot.
Often the image I am trying to load is coming from a samba share mapped to a directory like p:/
I have an issue that is ONLY happening when I execute the spring boot app as a JAR directly. If I execute it directly from STS using the tool executors it works fine. As well, the file is readable, viewable etc.
File f = new File("P:\\Stamps\\_Temp\\Img001.jpg");
BufferedImage image = ImageIO.read(f);
This will result in
javax.imageio.IIOException: Can't read input file!
at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1308) ~[na:na]
For debugging purposes I had it print out the .exist() and .canRead() - when executed in STS (Eclipse) these both return true, however from the JAR it will return false. More over if I try to access the image directly from a local folder (say c:\my-images) it works fine. So my assumption is there is some thing restricting access to these Network shared files when accessed from within a Jar (only).
I have not been able to find any reference information via searches to this on the usage of File so I am wondering if there is a spring boot configuration that is blocking this access (mainfest setting etc), or if it is a restriction of executing class byte-code from within a JAR?
So networked Mapped Drives in Windows can be accessed if you track back to the remote name and replace that drive letter with the appropriate mapping name. This thread covers an example where they do that: https://gist.github.com/digulla/31eed31c7ead29ffc7a30aaf87131def they key here is to replace the "P:" with "\server\path"
Again does not explain why this fails via Jar access vs. class exploded access, but at least it covers a workaround. For my use I might just simply use a mapping file since while I use the Network Mapping, I do not know how common this would be for other users and asking them to set some configuration in application.properties does not seem ridiculous for those cases. Still if anyone has insights into WHY we get different behavior inside and outside the Jar execution I'd be curious (or whether there is some spring-boot property in the manifest that needs to be set)

Spring Boot Static Resources From Controllers and Other Classes

I've been trying to solve this issue and I can't find the answer.
I've been having to use src/main/resources/public/whatever... in order to access static resources from class files. I was under the impression that if you use /static /public /resources... that Spring Boot already has those paths configured.
I'm lost at this point and not sure how I can access these files without having to prepend src/main/resources/public.
EDIT
Based on the suggestions regarding the static content, I created a private directory to protect files etc... that I don't want publicly available. And left css, images, etc... in the /public folder.
Thank you all for the suggestions and recommendations, I appreciate it!
After more searching I ran across an example that used the ClassLoader. I've only been using Spring for a short time and not aware of all the tools that are available, there might be a better way than this; if so please tell let me know.
ClassLoader classLoader = getClass().getClassLoader()
new File(classLoader.getResource('public/file-name-or-directory').getFile())
So far it looks like it's working, I need to do some further testing and will post back if any issues.
The directories you list, /static, /public and /resources, are for static content that will be served up by a web-application. Things like javascript files and images that will always be the same no matter who requests them and when, as opposed to JSPs for example that need to be processed on the server per request.
If you need to access a configured property in one of your Java classes (it must be a Spring bean), then you can put it in your application.properties file, and have Spring inject it using the #Value annotation.
Example code from Spring docs:
import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*
#Component
public class MyBean {
#Value("${name}")
private String name;
// ...
}
Spring Boot expect you to use for content served directly
src/main/resources/static
and to use for templating
src/main/resources/templates

Link to properties file outside webservice

I have a webservice that uses Java, REST, Jersey and runs on Tomcat8. The webservice requires access to a database. Depending on where we are in the process the we may be using a testdatabase, production database or something else. Ideally we would like to be able to set which database to use without requiring a code change and recompile.
The approach we have tried is to have a properties file defining the database parameters and use an environment variable to point to the file. This has proved troublesome, first we've had a hard time defining system properties on the Tomcat server that we can read from the application, also it seems like all the files will have to be defined on the classpath, i.e already configured ahead of time and part of the codebase.
This seems like fairly common scenario, so I'm sure there is a recommended way to handle situations like this?
Zack Macomber has a point here. Don't enable your app/service to look up its settings dynamically.
Make your build process dynamic instead.
Maven, Gradle and friends all provide simple ways to modify output depending on build parameters and or tasks/profiles.
In your code always link to the same file (name). The actual file will then be included based on your task and/or build environment. Test config for tests. Production config for production.
In many cases a complete recompilation is not necessary and will therefore be skipped (this depends on your tool, of course).
No code changes at all. Moreover the code will be dumb as hell as it does not need to know anything about context.
Especially when working on something with multiple people this approach provides the most stable long-term-solution. Customizable for those who need some special, local config and most important transparent for all who don't need or don't want to know about runtime environment requirements!
We have a similar case. We have created a second web service on the same endpoint (/admin) which we call to set a few configuration parameters. We also have a DB for persisting the configuration once set. To make life easier, we also created a simple UI to set these values. The user configures the values in the UI, the UI calls the /admin web service, and the /admin service sets the configuration in memory (as properties) as well as in the DB. The main web service uses the properties as dynamic configuration.
Note: we use JWT based authorization to prevent unauthorized access to /admin. But depending upon your need you can keep it unsecure, use basic HTTP auth or go with something more detailed.
Not sure if in this particular case it is wise, but it is possible indeed to create a .properties file anywhere on the filesystem - and link it into your application by means of a Resources element.
https://tomcat.apache.org/tomcat-8.0-doc/config/resources.html
The Resources element represents all the resources available to the web application. This includes classes, JAR files, HTML, JSPs and any other files that contribute to the web application. Implementations are provided to use directories, JAR files and WARs as the source of these resources and the resources implementation may be extended to provide support for files stored in other forms such as in a database or a versioned repository.
You would need a PreResources element here, linking to a folder, the contents of which will be made available to the application at /WEB-INF/classes.
<Context antiResourceLocking="false" privileged="true" docBase="${catalina.home}/webapps/myapp">
<Resources className="org.apache.catalina.webresources.StandardRoot">
<!-- external res folder (contains settings.properties) -->
<PreResources className="org.apache.catalina.webresources.DirResourceSet"
base="/home/whatever/path/config/"
webAppMount="/WEB-INF/classes" />
</Resources>
</Context>
Your application now 'sees' the files in /home/whatever/path/config/ as if they were located at /WEB-INF/classes.
Typically, the Resources element is put inside a Context element. The Context element must be put in a file located at:
$CATALINA_BASE/conf/[enginename]/[hostname]/ROOT.xml
See https://tomcat.apache.org/tomcat-8.0-doc/config/context.html#Defining_a_context

Spring .properties files purpose

I read #yorkw answer at this topic. He said:
The purpose of .properties file is to provide the capability of
configuring database connections at application runtime (for web
application, usually require restarting application container/server
after .properties file changes).
The question is if we can change properties on the fly without restarting container/server? Provide me an example please (I ask because in my demo it doesn't work, means value isn't changed).
I mean if we have some kind of admin tool than we can move all our configured settings to .properties files and change them via that admin tool.
Spring property files are designed to change the Spring Config of an application. The spring config is read when the spring container is initialised - this will form part of the application startup.
If a change is made to one of the spring config files (includes the *.properties files) the spring container would need to be reloaded to pick up the change.
Properties put into spring properties files should typically be properties that are tied to the life cycle of the application - i.e. the kind of properties that when changed require an application/spring container re-initialised - things like the database url/config etc.
So values that you want to change at runtime without requiring a restart of the application are not good candidates for placement in a spring properties file.

Where to put application-wide settings in Java (Servlet)?

In ASP.NET, there is web.config which can hold application-wide settings. Is there a corresponding file (residing outside of the war or jar archive) for a Java EE Servlet?
What I need is some place to point out a configuration file, which currently holds four attributes which in turn, taken together, leads to the database where the rest of the data and configuration is stored. (Server, database, username and password.) These values need to be easy to change without repackaging and redeploying the entire application, hence the configuration file, but hardcoding the path to the configuration file in the application (even if it is as a constant) seems far from optimal.
Any hints? I've tried Google but found very little that seemed relevant - and what I did find appeared hideously over-engineered for my needs.
In ASP.NET, there is web.config which can hold application-wide settings. Is there a corresponding file (residing outside of the war or jar archive) for a Java EE Servlet?
That's the web.xml. You can define settings as <context-param> entries.
<context-param>
<param-name>foo</param-name>
<param-value>bar</param-value>
</context-param>
It's available by ServletContext#getInitParameter(). The ServletContext is in turn available anywhere.
String foo = getServletContext().getInitParameter("foo"); // Contains "bar"
You can also access it by EL.
#{initParam.foo} <!-- prints "bar" -->
What I need is some place to point out a configuration file, which currently holds four attributes which in turn, taken together, leads to the database where the rest of the data and configuration is stored. (Server, database, username and password.) These values need to be easy to change without repackaging and redeploying the entire application, hence the configuration file, but hardcoding the path to the configuration file in the application (even if it is as a constant) seems far from optimal.
As per the emphasis, I'd use a properties file for this particular purpose which is then placed in a path outside the WAR. You just need to add this path to the Java runtime classpath. Then you can obtain it as classpath resource:
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("filename.properties"));
// ...
However, with the particular sole purpose to serve a DB connection, you're indeed better off with a servletcontainer-managed datasource as answered by Qwerky. All you possibly would need to configure is then just the datasource name.
If this is a web app then you'd be better served configuring the database connection as a resource on the server, then getting your app to retrieve it using JNDI. Your app server will have documentation on how to do this, its a basic task.
99% of serious web apps do this, the other 1% should.
You can have your application load an arbitrary external file by simply passing the path as a command-line parameter (to the servlet container startup script). Then store the values in the ServletContext

Categories