Springboot war project with tomcat and external application properties? - java

How can we provide an external application.properties, datasource.properties file with war project that runs on external tomcat? Is there any way to provide these files path as vm arguments in tomcat configuration. Also is it possible to get the values from these files as #Value annotation if use the above configuration?

Have a PropertySourcesPlaceholderConfigurer
<bean name="propertySourcesPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="location" value="file:${conf.dir}/app.properties" />
</bean>
where conf.dir you can pass it through JVM arguments like -Dconf.dir=yourconfigFilePath
and use like the following
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${dataSource.driverClassName}" />
<property name="url" value="${dataSource.url}" />
...
File app.properties
dataSource.driverClassName=org.apache.derby.jdbc.ClientDriver
dataSource.url=jdbc:derby://localhost:1527/dbpath

Related

Creating multiple instances of PropertyPlaceHolderConfigurer - Spring

I have 2 different applications deployed in application server (glassfish). One is a jar file and other is a war application. Both the applications refer to a single properties file (data.properties). To read the properties file, I have created a instance of Springs PropertyPlaceholderConfigurer in respective context files (business-beans.xml and applicationContext.xml). After deploying the applications, I am able to load the properties file in one application while the other web application throws "Could not resolve placeholder 'sw.throttle.enable'
Question -
How to solve the issue?
Is it incorrect load the same properties file at two locations?
Is there a way I load the properties file in one context and in the other bean definition file use the reference of the first one?
SnapShot of business.beans
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="placeholderPrefix" value="${sw." />
<property name="location" value="file:///etc/data.properties" />
<property name="ignoreResourceNotFound" value="true" />
</bean>
Property referenced as below in business.beans
<bean id="mService" class=" com.test.business.mService">
<property name="throttlingEnabled" value="${sw.throttle.enable}"/>
</bean>
Snapshot of applicationContext.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="placeholderPrefix" value="${sw." />
<property name="location" value="file:///etc/data.properties" />
<property name="ignoreResourceNotFound" value="true" />
</bean>
Property referenced as below in applicationContext.xml
<bean id="downloadService" class="com.test.downloadService"
init-method="startUp" destroy-method="shutDown"
p:throttlingEnabled="${sw.throttle.enable}" />
The application containing business.beans deploys well, but the application containing applicationContext.xml throw run time error "could not resolve placeholder sw.throttle.enable"
Note -
Both the applications are deployed in a OsGi Context.
Spring version is 3.0.1
Edit -
The applicationContext.xml has another bean defined as below. Could this be the cause?
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
</bean>
The issue was resolved by setting "ignoreUnresolvablePlaceholders" to "true". Apparently business.beans had nothing to do with the issue
Below is the modified configuration which solved the issue
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="placeholderPrefix" value="${sw." />
<property name="location" value="file:///etc/data.properties" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceHolders" value="true"
</bean>
Thanks StackOverflow for the answer +1

Spring UTF-8 message resource from external jar issue

I have a problem with UTF-8 message sources in Spring MVC application. I've tried two implementations of AbstractMessageSource: ResourceBundleMessageSource and ReloadableResourceBundleMessageSource. I have an external jar with i18n messages contained in com.mypackage.i18n package
The configuration for ResourceBundleMessageSource:
<bean id="propertiesMessageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="com.mypackage.i18n.messages" />
<property name="useCodeAsDefaultMessage" value="true" />
</bean>
This configuration loads finds and loads properties, but fails with UTF-8, because this implementation just doesn't support UTF-8.
The configuration for ReloadableResourceBundleMessageSource:
<bean id="propertiesMessageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="com.mypackage.i18n.messages" />
<property name="useCodeAsDefaultMessage" value="true" />
<property name="fileEncodings" value="UTF-8" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
This configuration doesn't find properties. I know that this implementation to have reloadable resources needs properties to be located somewhere in WEB-INF directory and it doesn't restrict resources to be located somewhere else if you don't need reloading of resources.
According to the class java:
Note that the base names set as "basenames" property are treated in a slightly different fashion than the "basenames" property of ResourceBundleMessageSource. It follows the basic ResourceBundle rule of not specifying file extension or language codes, but can refer to any Spring resource location (instead of being restricted to classpath resources). With a "classpath:" prefix, resources can still be loaded from the classpath, but "cacheSeconds" values other than "-1" (caching forever) will not work in this case.
Can someone advice me how to solve the problem: I need to use another approach or somehow modify config of ReloadableResourceBundleMessageSource to find resources from jar?
I've found a solution. Proper ReloadableResourceBundleMessageSource configuration looks like this:
<bean id="propertiesMessageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:com/mypackage/i18n/messages" />
<property name="useCodeAsDefaultMessage" value="false" />
<property name="fileEncodings" value="UTF-8" />
<property name="defaultEncoding" value="UTF-8" />
<property name="cacheSeconds" value="-1"/>
</bean>

Loading Bean property values from context.xml

Currently we are loading our JDBC source values from properties file as per following:
<context:property-placeholder location="classpath:master.properties" ignore-unresolvable="true" />
<bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource"
destroy-method="close">
<property name="driverClass" value="${database.driver}" />
<property name="jdbcUrl" value="${database.url}" />
<property name="username" value="${database.user}" />
<property name="password" value="${database.password}" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="idleMaxAge" value="240" />
<property name="maxConnectionsPerPartition" value="2" />
<property name="minConnectionsPerPartition" value="2" />
<property name="partitionCount" value="3" />
<property name="acquireIncrement" value="10" />
<property name="statementsCacheSize" value="50" />
<property name="releaseHelperThreads" value="3" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"
scope="singleton">
<property name="targetDataSource">
<ref local="mainDataSource" />
</property>
</bean>
This seg. works fine with classpath based app.properties file and get rid of app.properties.
We would like to load this values from context.xml (either placed in META-INF or $CATALINA_HOME/conf/context.xml). It will help us load proper values at prod/staging servers.
Will appreciate any help or alternate method/suggestions. (If similar question is already answered, please share the link)
Thanks!
As Alan Hay mentioned you could externalize the datasource configuration into Tomcat's own context.xml and then have Spring do a JNDI lookup to retrieve it. This is an approach that I've commonly used on some of the projects I've worked on.
The pieces you need to put in place to achieve would be:
1. Add the datasource configuration to $CATALINA_HOME/conf/context.xml
<GlobalNamingResources>
<Resource type="javax.sql.DataSource"
name="dsName"
factory="com.jolbox.bonecp.BoneCPDataSource"
driverClassName="your.driver.classname"
jdbcUrl="your:driver:url"
username="username"
password="password"
idleMaxAge="240"
idleConnectionTestPeriod="60"
partitionCount="3"
acquireIncrement="10"
maxConnectionsPerPartition="2"
minConnectionsPerPartition="2"
statementsCacheSize="50"
releaseHelperThreads="3" />
</GlobalNamingResources>
2. Add a resource link in the application's META-INF/context.xml
<Context path="/YourApp">
<ResourceLink description="Datasource for YourApp"
global="jdbc/dsName"
name="jdbc/dsName"
type="javax.sql.DataSource" />
</Context>
3. Modify the Spring config to lookup the datasource in JNDI
<beans xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/jee classpath:/org/springframework/ejb/config/spring-jee-3.0.xsd">
<jee:jndi-lookup id="dataSource"
jndi-name="java:comp/env/jdbc/dsName" />
4. Move the driver and datasource jars
Since the datasource configuration is now container managed, you should place the database driver and datasource jars into $CATALINA_HOME/lib so they are available to Tomcat when it creates the datasource. These jars should no longer need to reside in the WEB-INF/lib of your application.
The way I do it, I have default properties file in my classpath, and I have one properties file that I use to override those default (development) properties:
in my spring file:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/>
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
<value>classpath:camel.properties</value>
<value>classpath:email.properties</value>
<value>${external_config}</value>
</list>
</property>
</bean>
In development, I use the properties file in the classpath.
On the different envionment, staging, integration, production we start the application with the following parameter:
-Dexternal_config=file:c:/staging.properties
That way I have the same build for all environments and can choose the location of my overriding properties file.
So, ${external_config} is a place holder for a configuration file that depends on the environment.
Spring looks for an environment variable named external_config, that variable should point to a configuration file. If the file is not found, Spring ignores it with ignoreResourceNotFound = true;
Spring 3.1 introduced support for environment specific profiles. Define some profiles, associate them with some properties files e.g. test.properties, live.properties and set an environment variable on server start -Dspring.profiles.active=Test. Clean and simple.
http://spring.io/blog/2011/02/11/spring-framework-3-1-m1-released/
Other option as hinted at above is to shift to a container managed Connection Pool looked up via JNDI i.e define a JNDI datasource named xyz the same on each server and then:

How to read jBoss configuration files in Spring?

I want to have a project independent configuration file that I can access from different projects. What I'm currently trying (and does not give me good results at all):
<bean id="wroProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="${JBOSS_HOME}/standalone/configuration/wro.properties" />
</bean>
I use Spring 3 and JBoss 7.1. My configuration files are under jboss/standalone/configuration/....properties. Besides that I want to read message files from that same directory with:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="messages,local" />
<property name="useCodeAsDefaultMessage" value="true" />
</bean>
Currently it looks for messages.properties and local.properties in src folder?
This is the solution I ended up using, which is platform independent and portable:
<bean id="wroProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="file:#{systemProperties['jboss.home.dir']}/standalone/configuration/wro.properties" />
</bean>
The configuration of the message source is identical.
A ResourceBundleMessageSource uses the basenames provided to (and the locale) to build a resource name (ex. message.properties) which is eventually (in the call stack) used by java.util.ResourceBundle.Control#newBundle(...). This resource name is then looked for on the classpath starting at its root (ex. /message.properties).
If you're on an IDE like Eclipse, your classpath very likely starts at src.
If jboss/standalone/configuration/... is on your classpath as well and the properties file are in there, you can change the basenames to
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="jboss/standalone/configuration/messages,jboss/standalone/configuration/local" />
<property name="useCodeAsDefaultMessage" value="true" />
</bean>

How to inject properties into a spring bean from Main class

I'm using spring with my application, and I'm able to inject some properties from some file on a class path into my app and everything works perfectly. i.e.
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchContextAttributes" value="true" />
<property name="contextOverride" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:application.properties</value>
</list>
</property>
</bean>
Now I can use ${any.property.from.application.properties} in my spring context. And in my main class :
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("appContext.xml");
It works as well, my question is how do I inject property file location in the spring context without it being there at first, I want to make my app configurable. If I'm executing my app from C:\dir or /user/home/dir I assume that in the application context the value should be either C:\application.properties or /user/home/dir/application.properties
I had a similar problem sometime back. My requirement was the the property files is not bundled inside the application (and hence not in classpath). The file could be at any location in file system.
Here is how I solved it,
Define an environment variable the value of which points to the location of application.properties.
Lets say we have a env variable APP_PROP_HOME with value /user/home/dir/
Now while defining ServletContextPropertyPlaceholderConfigurer, define the locations as follows
I am reusing your example
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchContextAttributes" value="true" />
<property name="contextOverride" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>file://${APP_PROP_HOME}/application.properties</value>
</list>
</property>
</bean>
The Spring resolves ${APP_PROP_HOME} to the value stored in corresponding env property and your application is configured at runtime.
If I am reading your question correctly, You want to use an external properties file(i.e File is not in the application runtime class path). If that is the case you need to use the file tag
<value>file:///c:\application.properties</value>
You can use #Value to inject values from the env. Example:
private someFoo;
#Value("${systemProperties.someFoo}")
public void setSomeParam(String someParam) {
this.someFoo = someParam;
}

Categories