Loading Bean property values from context.xml - java

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:

Related

Springboot war project with tomcat and external application properties?

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

Setup database connection with tomcat 7

I have a working programm with MySQL, Spring and a tomcat server, but the database connection is in spring-database.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="baseController" class="com.afterguard.sailplanner.controller.BaseController">
<property name="dao" ref="daoImpl" />
</bean>
<bean id="daoImpl" class="com.afterguard.sailplanner.dao.DaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/sailplanner" />
<property name="username" value="sailplanner" />
<property name="password" value="sailplanner2" />
</bean>
</beans>
In order to deploy this program on a server now, I would need bring this database connection to the server, e.g. the context.xml oder server.xml.
How do I do I setup my tomcat 7 server to connect to the database and tell my application, that the database connection is setup in the tomcat?
You have to create a dataSource with JNDI :
First, in your spring bean configuration (spring-database.xml) add this :
<beans:bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<beans:property name="jndiName" value="java:comp/env/jdbc/myDB"/>
</beans:bean>
Second, create the jndi resource in the file apache-tomcat/conf/server.xml :
<Resource name="jdbc/globalDB"
global="jdbc/globalDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/sailplanner"
username="sailplanner"
password="sailplanner2"/>
Finally, create the resource link in the file apache-tomcat/conf/context.xml :
<ResourceLink name="jdbc/myDB"
global="jdbc/globalDB"
auth="Container"
type="javax.sql.DataSource" />
For Sql Server I do the following configuration:
You must be using Tomcat to manage connections. To do so we will be adding data sources to the TOMCAT_HOME/conf/context.xml file
<Context>
<Resource
name="jdbc/Oltp1"
auth="Container"
type="javax.sql.DataSource"
driverClassName="net.sourceforge.jtds.jdbc.Driver"
url="jdbc:jtds:sqlserver://localhost:3306/sailplanner"
username=user
password=password
testOnBorrow="true"
testOnConnect="true"
validationQuery="SELECT 1"
removeAbandoned="true"
removeAbandonedTimeout="30"
/>
</Context>
SQL Server JDBC driver (jtds-version.jar) must be copied to the TOMCAT_HOME/lib folder (if it is not there already)
If there are multiple data sources (OLTP1, OLTP2, etc) we will be adding multiple <Resource/> tags
You will be using the following JNDI names to name our data sources:
jdbc/Oltp1 – main OLTP database
jdbc/Oltp2 – secondary database
You will be configuring data sources so connection pool will know how to test connections and what to do with closed connections:
testOnBorrow="true"
testOnConnect="true"
validationQuery="SELECT 1"
removeAbandoned="true"
removeAbandonedTimeout="30"
Tomcat must be restarted when new resource is added or existing resource is modified.
application.properties file
Important! This file is deployed as a part of the classpath (src/main/resources/application.properties), not the external application.properties with externalized properties deployed to the TOMCAT_HOME/conf
You will be added the following property to this file:
spring.datasource.jndi-name=java:comp/env/jdbc/Oltp1
You need to remove all other spring.datasource properties from the application.properties if any exist
Important! You had to remove all spring.datasource properties from the external configuration file (application.properties deployed to the COMCAT_HOME/conf)
the database connection properties you should put in a property file and and depending upon your requirement you can change values in property file
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.databaseurl}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
the property file
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.databaseurl=jdbc:mysql://localhost:3306/sailplanner
jdbc.username=root
jdbc.password=root
and to include this file in xml use
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:applications.properties</value>
</list>
</property>
</bean>

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>

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>

Spring loading application.properties based on tomcat servlet context definition

I need to have a development and production settings for our spring project. I understand that you can use profiles for spring but that is not something that we can do.
What I want to do is place on the development environment a test-application.properties file and on production a prod-application.properties file. In the tomcat context definition we sent the following:
<Context>
<context-param>
<param-name>properties_location</param-name>
<param-value>file:C:\Users\Bill\test-application.properties</param-value>
</context-param>
</Context>
And we can have the value changed for the production servers. In the spring config we have something like this:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>${properties_location}</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false" />
</bean>
But we keep getting errors like:
org.springframework.beans.factory.BeanInitializationException: Could
not load properties; nested exception is
java.io.FileNotFoundException: Could not open ServletContext resource
[/${properties_location}]
Any ideas on how to solve?
One feature of PropertyPlaceholder is that you can define multiple resource locations.
So for example you can define your-production-config.properties along with file:C:/Users/${user.name}/test-application.properties
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:your-production-config.properties</value>
<value>file:C:/Users/${user.name}/test-application.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="ignoreResourceNotFound" value="true"/>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
</bean>
for production you need to place prod configuration into classpath somewhere(really not important where exactly, just classpath) - for local env you can use convension like this file:C:/Users/${user.name}/test-application.properties
<context:property-placeholder location="file:${catalina.home}/conf/myFirst.properties" ignore-unresolvable="true" />
<context:property-placeholder location="classpath:second.properties" ignore-unresolvable="true" />
I do it like above. The catalina.home variable allows the properties file to be lcoated in the tomcat home conf directory.
I ended up solving it by not using context params. Instead we have defined
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:application.properties</value>
<value>file:C:\Users\Bill\prod-application.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="ignoreResourceNotFound" value="true"/>
</bean>
This way tries to load both files. On test servers we do not have the prod file so it is not loaded. On prod server the prod-application.properties file exists and overrides the test which is in the classpath. Cumbersome but works!
Personally, try to avoid specify locations. I think best thing for you is to use JNDI to achieve this.
In tomcat/conf/server.xml
<Resource name="jdbc/prod" auth="Container"
type="javax.sql.DataSource" driverClassName="${database.driverClassName}"
url="${database.url}"
username="${database.username}" password="${database.password}"
maxActive="20" maxIdle="10"
maxWait="-1"/>
and In tomcat catalina.properties (If using Oracle XE otherwise change it accordingly):
database.driverClassName=oracle.jdbc.driver.OracleDriver
database.url=jdbc:oracle:thin:#//localhost:1521/XE
database.username=user
database.password=password
In your application create properties file in your classpath named jdbc.properties and put followings (If using Oracle XE otherwise change it accordingly)
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:user/password#//localhost:1521/XE
then In Spring applicationContext.xml
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/prod" />
<property name="defaultObject" ref="dataSourceFallback" />
</bean>
<bean id="dataSourceFallback" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="poolPreparedStatements">
<value>true</value>
</property>
<property name="maxActive">
<value>4</value>
</property>
<property name="maxIdle">
<value>1</value>
</property>
</bean>
use :
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>C:/Users/Bill/test-application.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false" />
</bean>
Remove below code from web.xml
<Context>
<context-param>
<param-name>properties_location</param-name>
<param-value>file:C:\Users\Bill\test-application.properties</param-value>
</context-param>
</Context>
if you are using Tcserver and server.xml to configure Resources like database,queues etc you can using the com.springsource.tcserver.properties.SystemProperties
Delcare this listener in server.xml like below
<Listener className="com.springsource.tcserver.properties.SystemProperties"
file.1="${catalina.base}/conf/password.properties"
file.2="${catalina.base}/conf/server.properties"
immutable="false"
trigger="now"/>
Now you can externalize the properties to the two files password.properties and server.properties.

Categories