How to override spring property param with Tomcat? - java

I have defined property in Spring application.
#Configuration
public class WebappConfiguration {
#Value("${ext.storage.path}")
private String extDirectoryPath;
public String getExtDirectoryPath() {
return extDirectoryPath;
}
}
Default value for ext.storage.path property is defined in application.properties file.
application.properties
ext.storage.path=/home/user/ext/
When I deploy WAR to tomcat with VM options -Dext.storage.path=/var/webapp-data/, this value is loaded successfully. But I would like to load property values more smarter from context files for every environment.
So I deploy the WAR to ROOT of Tomcat, name of WAR file is ROOT.war and it is exploded to ROOT directory. I created context file on path {CATALINA_BASE}/Catalina/localhost/ROOT.xml with following content.
ROOT.xml
<Context
docBase="/opt/webapp-tomcat/webapps/ROOT.war"
path=""
reloadable="true">
<Parameter name="ext.storage.path" value="file:/var/webapp-data/" override="true"/>
</Context>
Unfortunately, the param is not loaded according to the way, it has default value from application.properties.
EDIT:
After a little investigation, I put the Parameter into main context.xml file of Tomcat and the value is overridden.
<Parameter name="ext.storage.path" value="file:/var/webapp-data/" override="true"/>

The best technique I know of is to put environment-specific stuff into Tomcat's conf/context.xml file.
<Environment name="myApp/extStoragePath" type="java.lang.String" value="/var/data/myapp"/>
This defines a JNDI variable that you lookup in your code with something like this:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
String extStoragePath = (String) envCtx.lookup("myApp/extStoragePath");
You can also use this technique to load complex objects like database datasources etc. It has huge advantages:
The configuration is personal to the environment, not the application
You don't need to mess with Tomcat startup scripts
The same binary will run in DEV, UAT and PROD without modification. This much simplifies the build process.
Also, Spring provides a org.springframework.jndi.JndiObjectFactoryBean which can access JNDI from your spring configuration files.

Related

How to externalize application.properties in Tomcat webserver for Spring?

SpringApplication will load properties from application.properties
files in the following locations and add them to the Spring
Environment:
- A /config subdirectory of the current directory.
- The current directory
- A classpath /config package
- The classpath root
The list is ordered by precedence (properties defined in locations
higher in the list override those defined in lower locations).
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files
Question: when running a war file on a tomcat server: how can I add an additional location for the application.properties outside the classpath or the tomcat container, like d:\application.properties?
The custom location should get highest precedence regarding the locations above.
Problem is: I could of course add a /config folder inside my exploded war in the tomcat webapps folder, but then I'd lose any custom configuration if the webapps folder is cleaned and war is redeployed.
Thus I'd like to add an additional location outside.
For me the easiest way to do it, was to place a context file inside Tomcat's config folder. For example if your application is running under root path (eg. http://your_domain.com/) you need to create a file [path_to_your_tomcat]/conf/Catalina/localhost/ROOT.xml. If your application runs in a different path, for example http://your_domain.com/example_path the file should be named like this [path_to_your_tomcat]/conf/Catalina/localhost/example_path.xml. Inside this file you can specify a path to the external application.properties file that can be placed anywhere on your hard drive.
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Environment name="spring.config.location" value="file:/path/to/your/application/properties/file/" type="java.lang.String"/>
</Context>
You can set a spring_config_location environment variable pointing to the folder that contains your application.properties file.
In the case of Tomcat you can do this by adding the following line to your <TOMCAT_HOME>/bin/setenv.sh file (create the file if missing):
export spring_config_location=/usr/local/tomcat/conf/
Place the properties file in that folder. In case you have multiple apps you can set the name of the properties file of each app to be unique. For a Spring Boot App I have done it like this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
System.setProperty("spring.config.name", "my-app");
SpringApplication.run(MyApplication.class, args);
}
}
This will pick the new name when run with BOOT. To have the name configured when deployed on Tomcat too, overwrite configure of SpringBootServletInitializer like so:
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class).properties("spring.config.name: my-app");
}
}
Then name your properties file like: my-app.properties. Instead of the default name Spring will look for that. You can put all your apps properties files in the specified folder, /usr/local/tomcat/conf/ in our sample. Your external properties will get precedence. See here for priorities: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
UPDATE
Since Spring Boot 2 the behavior of spring_config_location has changed (from the migration guide):
it previously added a location to the list of default ones, now it
replaces the default locations. If you were relying on the way it was
handled previously, you should now use
spring.config.additional-location instead.
So based on your use case you should consider which of the properties to set as an environment variable. The new one should look like spring_config_additional-location in setenv.sh.
Where files are looked up from is described in the reference documentation too.
In tomcat/bin create setenv.sh file and in file you need to give below line in file for default properties for all the wars, And save it and restart tomcat.
export SPRING_PROFILES_ACTIVE=dev
I had to do it several times and the best approach I found is to configure an external directory as classpath resource in the container:
Then, in the directory place the resources you want to externalize and everything will run fine. To load the resource in spring, you could do this way:
<beans:bean id="externalProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location" value="classpath:[my-application-name]/applicationProperties.properties" />
<beans:property name="placeholderPrefix" value="!applicationProperties{" />
<beans:property name="placeholderSuffix" value="}" />
</beans:bean>
You can see that, as you said that you might want to have multiple applications deployed in each tomcat, you could simply create a directory structure in the folder you set in classpath, to maintain different application.properties for each of your war applications
In case you want to maintain dinamically the application name section in your Spring configuration, you could do it in several ways, during packaging stage in maven or even using the application context path
I finally added the following property to externalize eg secure properties:
spring.config.additional-location=/etc/tomcat/<appname>/application-production.properties
if anyone is looking for a linux solution, This is working for us:
edit tomcat startup.sh
add:
export spring_config_location=/<YOUR_PATH>/application.properties
example:
export spring_config_location=/app/conf/application.properties
for tomcat 9 on Ubuntu 18.04 and spring boot 2, create setenv.sh file under $CATALINA_HOME/bin/ working for me:
#!/bin/bash
export spring_config_additional_location="/opt/tomcat/latest/conf/application.properties"
don't forget to set file permission if needed
Using Tomcat 9.0.27
Running Spring boot 2.2.1
I did setup setenv.sh and added the line
export spring_config_location=/<PATH_TO_CONF_DIR>/application.properties
and got it to work..

How to place configuration file for Tomcat with several virtual hosts?

I've got Tomcat (7 or 8) with two virtual hosts with two clones of application should work.
Each application should have it's own configuration file. And it shouldn't be placed in *.war - only somewhere in server environment.
When I have a single application in Tomcat, I can place configuration file in
<context:property-placeholder location="file:${catalina.home}/conf/myapp.properties"/>
This is how Spring will find my configuration file due to applicationContext.xml.
But when I have two hosts, I should place my configuration files in different directories.
I've added Context attribute in Host in server.xml
<Context docBase="" path="XXX">
<Environment name="app.name" value="myapp1" type="java.lang.String" override="false"/>
</Context>
Here first host gets environment variable "app.name" like "myapp1". Second host gets this variable with "myapp2" value.
I've modified
But Tomcat falls with FileNotFoundException
Context initialization failed
org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: ...\conf\${app.name}\myapp.properties (The system cannot find the path specified)
Why?
The environment property that you have configured is a JNDI lookup property.
I do not think it can be read as a normal system property as in the case of
"CATALINA_HOME".
Environment - Configure names and values for scalar environment entries that will be exposed to the web application through the JNDI InitialContext (equivalent to the inclusion of an <env-entry> element in the web application deployment descriptor).

Static ressources: where define context aliases with tomcat

I am looking for a way to access static resources (e.g. video files) from my web app JEE.
In my local environment, I added an aliases attribute in the context of my webapp under the config of my tomcat server in Eclipse. This works verywell.
Doing that my context in Eclipse Tomcat server.xml is:
<Context path="/maWebApp" docBase="path/vers/ma/webApp" aliases="/video=/chemin/sur/mon/PC">
Now I want to do the same thing in my production server. But:
under this server (linux, tomcat7), the file etc/tomcat7/server.xml doesn't contains any "Context" for my webapp. I suppose that the context is created automatically during webApp deployment
if I add a "Context" for my application, in order to define the "aliase" attribute, my server tomcat doesn't restart anymore.
So my question is: where should I define the "aliase" attribute when I use Tomcat7 and when I deploy my application with .war generated from Eclipse.
Important note: I don't want to manage the aliases in the webapp, because the aliases change depending on the server (local dev or prod).
Thank you very much for any advise and best practise,
Have a good day!
Adrien
You should be able to add a context attribute to your server.xml.
https://tomcat.apache.org/tomcat-4.1-doc/config/context.html
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="on" /> <Listener
className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
/> <Listener
className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"
/>
<Context path="/maWebApp" docBase="path/vers/ma/webApp"
aliases="/video=/chemin/sur/mon/PC" />
</Server>
My tomcat is starting fine with this.
What error do you have when starting it with the "< Context .. />" attribute ?
Take a look over here: https://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Defining_a_context
Defining Context elements in server.xml is discouraged. Instead put context.xml in
$CATALINA_BASE/conf/[enginename]/[hostname]/
enginename will most likely be Catalina, so e.g if your tomcat directory is /opt/tomcat7/ and your hostname is www.mysite.com then put the context in this directory:
/opt/tomcat7/conf/Catalina/www.mysite.com
And rename your context file maWebApp.xml
Update: Unless you need the static resources to be available to your app, and if only you need a virtual directory for visitors to access static resources you do not need the aliases attribute. Create a context in a file named video.xml in the same directory as above:
<Context docBase="/chemin/sur/mon/PC/" path="/video/"></Context>
Static resources will then be available at www.mysite.com/video

java:comp/env is not bound

I currently have a few libraries in my $CATALINA_HOME/lib directory to handle some system/DB authentication. One of those files foo.jar contains a class that extends
org.apache.catalina.realm.DataSourceRealm
and is being used to authentication. This file needs to access a few properties that I'm storing in server.xml However, when attempting to access it I'm getting an error. The segment of code is as follows
try{
Context ctx = new InitialContext();
Context envCtx = (Context)ctx.lookup("java:comp/env");
String property = (String)envCtx.lookup("property");
} catch(Exception ex) {}
Any ideas?
You also need to have the associated ResourceLink in the context.xml for your app. For example, if your webapp is named foo, there should be a file called conf/Catalina/localhost/foo.xml with a
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Context>
<Context antiJARLocking="true">
<ResourceLink name="property"
type="classname"
global="propertyNameInGlobalNamingResources" />
...
</Context>
If you include a META-INF/context.xml with these links in your war file, tomcat will copy it out to the conf/Catalina/localhost/foo.xml the first time the app is deployed (though not any time after that as you are intended to make changes there and be able to deploy again without clobbering customization)

customize web application root context in apache

I have web application abc.war and I want to deploy it on Apache Tomcat.
The problem is that, by default, the path to this application is http://<server-name>/abc
but I want to access it as http://<server-name>/xyz.
I put into WAR's META-INF folder the file context.xml that is :
<Context path="/xyz" docBase="abc" override="true" />
The application WAR abc.war is located under %CATALINA_HOME%\webapps and it is extracted to %CATALINA_HOME%\webapps\abc folder.
Also, while deployment, the file context.xml from abc/META-INF is copied to %CATALINA_HOME%\conf\Catalina\localhost as abc.xml
It seems that this should work, but I still can't access my application through http://<server-name>/xyz, but only through http://<server-name>/abc
In addition, I still see in apache log the following line while deployment of abc.war :
context path = /abc
Could anybody, please, help while this doesn't work, or tell if there is any way of deploying of web application on apache such that application could be accessed by customized path (that does not relate to war-file name) ?
Thanks in advance.
Take a look at the docs:
The context path of this web application, which is matched against the beginning of each request URI to select the appropriate web application for processing. All of the context paths within a particular Host must be unique. If you specify a context path of an empty string (""), you are defining the default web application for this Host, which will process all requests not assigned to other Contexts.
The value of this field must not be set except when statically defining a Context in server.xml, as it will be inferred from the filenames used for either the .xml context file or the docBase.

Categories

Resources