How to externalize properties file for web application(war)? - java

I have developed a webapplication which is having jsp and java code. Right now I have placed all the key-value into a env/lifecycle specific properties file (like conf-dev.properties,conf-stg.properties,conf-prod.properties).
I want to externalize these properties file so that it can be placed outside of war(without effecting the war).
right now war file is tightly coupled with properties file. if i have to modify any thing i have to build and make war and deploy.
I have very limited access on deployment server machine (only have access for one folder where i can put my configuration files) & deployment process is handled by CI(jenkin & automated script).
I explored on internet and came to know that we can achieve this using spring, would like to know what is the best way to achieve this?

As you are using Spring I suppose you already use PropertyPlaceholderConfigurer. If not you should ;)
The location of a property file can be anything that can be resolved as spring Resource. This includes classpath, servletcontext and also file references as URIs (file:///... For absolute paths)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="${config.file.location}" />
</bean>

If I understand your question, then you can use Class.getResourceAsStream(String) the linked Javadoc says (in part)
This method delegates to this object's class loader. If this object was loaded by the bootstrap class loader, the method delegates to ClassLoader.getSystemResourceAsStream(java.lang.String).

The better way to externalize env specific properties is to use "user.home" or "user.dir".

Thanks #Martin F..
Resolved:This is the final one i used and its working fine in dev,stage Env.
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="false"/>
<property name="order" value="1"/>
<property name="locations">
<list>
<value>classpath:conf-${cisco.life}.properties</value>
<value>file:///${openshift.home}/data/conf-${cisco.life}.properties</value>
<value>file:${openshift.home}/data/conf-${cisco.life}.properties</value>
</list>
</property>
</bean>.
and i used script action hook in openshift to set the lifecycle on system level.
appname=echo $OPENSHIFT_APP_NAME
case "$appname" in
*dev)export JAVA_OPTS_EXT="${JAVA_OPTS_EXT} -Dcisco.life=dev";
echo "setting up env life dev for " $appname
;;
*stage)export JAVA_OPTS_EXT="${JAVA_OPTS_EXT} -Dcisco.life=stg;
echo "setting up env life as stg for " $appname.

Related

set properties by vm-option in tomcat (local)

I have some question.
Before format My intellij is work very well.
I manage some value for multiple environment and control by some properties file.
Here is some sample.
I have some file for properties like this.
production.properties
test.properties
test-remote.properties
And my tomcat configuration (of course VM options) set like this.
... -Drun.mode=test ...
And here is my root-context.xml.
...
<!-- Root Context: defines shared resources visible to all other web components -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:${run.mode}.properties</value>
</list>
</property>
</bean>
...
Before format, as i say, if change the tomcat's vmoption -Drun.mode=SOMETHING, that works. but after format, there is only work test properties.
have you guys any idea about it? I can't imagine even how can i fix it.
Very interesting point is that. test is work well.
But there is not hardcoding for just only use test.properties.
There is a different that tomcat's version.
usually i use 8.5.x and after format i used 9.0.x and there have some ClassNotFound Exception.
After change to use tomcat 8.5.x problem is gone.
But have to figure out why 9.0.x is not running for Properties file select by VM options.

Not able to find Spring "classpath:" location

I have a Spring Project where I am using bean configuration file
beans.xml.Inside the bean Configuration file, i have defined some properties for a PlaceHolder which refers to classPath...While the application is running, the properties are getting loaded from /unknownPath/Dev/Loc1/System.properties
Where
${BUS_ENV}=Dev
${LOCATION1}=Loc1
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:${BUS_ENV}/${LOCATION1}/system.properties</value>
<value>classpath:${BUS_ENV}/lbsprocessor.properties</value>
</list>
</property>
<!-- Force system properties to override any deployed runtime properties -->
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
I didn't specify classpath while running my project in IDE
I don't have those files in my resource folder
There are around 65 such files exists(for various reasons) as Dev/Loc1/System.properties
I am not able to find from which location the properties are getting referred. Even after debugging, I couldn't find out what classpath refers to. Please help me with figuring out
If you are using eclipse IDE right click on your project select properties then select Java Build Path. On first tab Source there is one input named Default Output folder that value is your classpath. Check all your properties files are there in that path.
Referring to your point 2 problems might be in these line
<value>classpath:${BUS_ENV}/${LOCATION1}/system.properties</value>
<value>classpath:${BUS_ENV}/lbsprocessor.properties</value>
You are using classpath for file location which means these properties file have to be in the .war file at /Dev/Loc1/System.properties
If properties files are outside of project may be at system level you can access them like this
<value>file:${BUS_ENV}/${LOCATION1}/system.properties</value>
<value>file:${BUS_ENV}/lbsprocessor.properties</value>
eg:
<value>file:/home/testuser/system.properties</value>
I am using Mac OS ,in this we are storing the Configurations as a jar file under
/Library/Java/Extension.So java is directly referring classpath to that location by default.

Did the contextPath is available as a placeHolder for defining the path of a properties files

Let start with some context:
Tomcat container
Autodeploy of war
No Context.xml in tomcat (at least yet)
Two (identical) war deploy with two different context (ROOT.war, test.war, why not one war per feature branch)
Some properties should be different (overwrite) for the two war.
I use something like this for properties:
<bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true"/>
<property name="locations">
<list>
<value>classpath:internal-common.properties</value>
<value>classpath:${server.contextPath}/context.properties</value>
<value>classpath:dev-overwrite.properties</value>
</list>
</property>
The internal-common and the dev-overwrite work as expected.
I can't found the correct placeHolder (not ${server.contextPath} at least) to have an external properties per context.
What I think can't work and are on lot of answer related to this:
use "env". The two WAR share the same tomcat instance, so the same env
system properties, same reason
have different properties name, I don't want as I want the same war
use the from tomcat. If I can avoid it, this will simplify my process.
extend spring properties object will work also, but I wanted to be sure they are nos easier/simplest solution for this.
Ideally, any placeholder with contextPath information will do the trick.

log4j log file relative path, CONSOLE APPLICATION(jar)

I'm using Spring in a Console java application. I'm using PropertyPlaceholderConfigurer to load database details, and it works flawlessly:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:/appdata/configs/myapplication/connection.properties" />
</bean>
So the root dir will be relative to where the jar is located. I need the same functionality with log4j.properties
log4j.appender.file.File=/appdata/configs/myapplication/myapplication-log.log
How can I achieve this without defining environment variables?
Just tried it like this:
log4j.appender.file.File=\\appdata\\configs\\myapplication\\myapplication-log.log
And it works fine.

Reloading of properties file which is loaded using setBundle

I was hoping for a little help with a problem I am having involving properties files in Spring. So the setup I have is like so:
opto-mapping.properties – this is located in my src folder and contains translations for my optimised resources like so:
generic-min.css=4037119659.css
This properies file is updated every time the build ‘optimise’ is run. I then use
<fmt:setBundle basename="opto-mapping" />
To import my properties file in my desired jsp. Then referencing the content by using:
<fmt:message key='generic-min.css' />
This all works beautifully except that the properties file requires a tomcat restart to be reloaded. I dont want to have to start taking sites down everytime a resource is updated. I would like the properties file to automatically reload every so often.
I did attempt to update an existing bean in my spring-context.xml to reload this properties file like I do with translations but this has not worked - more than likely because of the opto-mapping.properties files location - but you see it needs to be in that location to load using fmt:setBundle.
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="cacheSeconds">
<value>1</value>
</property>
<property name="basenames">
<list>
<value>WEB-INF/translations/translations</value>
<value>WEB-INF/classes/opto-mapping</value>
</list>
</property>
</bean>
Any help or a point in the right direction would be greatly appreciated in this difficult time.
I hope all this makes senese and many thanks in advance!
G.
Thank you both for your responses. I have now got this working and thought I would share the wealth.
So, I moved my properties file out of the src folder and into WEB-INF/properties.
I updated the following bean to load up the properties files:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="cacheSeconds">
<value>1</value>
</property>
<property name="basenames">
<list>
<value>WEB-INF/translations/translations</value>
<value>WEB-INF/properties/opto-mapping</value>
</list>
</property>
</bean>
Now, previously I was using setBundle to load into my properties file like this:
<fmt:setBundle basename="opto-mapping" />
But I found that obviously my properties file wasnt being loaded anymore because I had moved it. But because of my bean setup the new properties file was being loaded but my setBundle was overwritting that.
So, the solution was to remove the setBundle and now my properties file is reloading!
Thanks again!
Tomcat will not reload resources that are on the classpath. This is stated in the javadoc for ReloadableResourceBundleMessageSource:
Since application servers typically cache all files loaded from the classpath, it is necessary to store resources somewhere else (for example, in the "WEB-INF" directory of a web app). Otherwise changes of files in the classpath will not be reflected in the application.
Such classpath locations include WEB-INF/classes, and will not be released.
Try moving opto-mapping.properties elsewhere (e.g. WEB-INF/messages), and try it then.
There are a few things you might try.
<fmt:setBundle> will eventually call ResourceBundle.getBundle(String, Locale, ClassLoader), where the string will be your basename and the classloader will be Thread.currentThread().getContextClassLoader(). If you're using JDK 1.6, you can try using ResourceBundle.clearCache(ClassLoader) to clear the bundle cache. It would make sense to do this in a servlet filter and combine it with some other logic to determine when the cache should be cleared.
Another angle is take more direct control over the loading of the properties file and the configuration of JSTL. Again, making use of a filter (ignoring exception handling):
ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
URL propsURL = ctxLoader.getResource("opto-mapping.properties");
URLConnection propsConn = propsURL.openConnection();
long propsLastModified = propsConn.getLastModified();
// decide if you want to reload...
propsConn.setUseCaches(false);
InputStream propsIn = propsConn.getInputStream();
ResourceBundle propsBundle = new PropertyResourceBundle(propsIn);
propsIn.close();
LocalizationContext propsCtx = new LocalizationContext(propsBundle);
ServletContext servletCtx = this.filterConfig.getServletContext();
Config.set(servletCtx, Config.FMT_LOCALIZATION_CONTEXT, propsCtx);
Then you can just use <fmt:message> in your pages. You can find the docs for LocalizationContext and Config in the JSTL API.
Lots of other variations are possible, but make sure to take a look at the newer ResourceBundle (including ResourceBundle.Control) additions to JDK 1.6, keep in mind the functionality of "lower-level" APIs like URLConnection, and become familiar with the more programmatic aspects of JSTL available through its API.

Categories

Resources