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.
Related
In my project, I have multiple context files.In that I am loading the property files using the property place holder like below.
Below are my context.xml files.
a.xml
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:${conf.path}/devconfiguration.xml" />
<!--<property name="location" value="file:${conf.path}/sitconfiguration.xml" />
<property name="location" value="file:${conf.path}/uatconfiguration.xml" />
<property name="location" value="file:${conf.path}/prodconfiguration.xml" />-->
</bean>
b.xml
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:${conf.path}/devconfiguration.xml" />
<!--<property name="location" value="file:${conf.path}/sitconfiguration.xml" />
<property name="location" value="file:${conf.path}/uatconfiguration.xml" />
<property name="location" value="file:${conf.path}/prodconfiguration.xml" />-->
</bean>
c.xml
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:${conf.path}/devconfiguration.xml" />
<!--<property name="location" value="file:${conf.path}/sitconfiguration.xml" />
<property name="location" value="file:${conf.path}/uatconfiguration.xml" />
<property name="location" value="file:${conf.path}/prodconfiguration.xml" />-->
</bean>
While taking war file each time we are going to change all context files. Is their any way to have one property place holder for entire project.
I tried but I am not able to load property files without using property place holder bean. Any help will be greatly appreciated.
You can import definitions of b.xml and c.xml into a.xml
<beans> // a.xml
...
<import resource="classpath:b.xml"/>
<import resource="classpath:c.xml"/>
Now all definitions of b.xml and c.xml will be available in a.xml
and you can just define them combined in any of xml's
<context:property-placeholder
location="classpath:a.properties,
classpath:b.properties,
classpath:c.properties"
ignore-unresolvable="true"/>
or if you are not using context namespace
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:a.properties</value>
<value>classpath:b.properties</value>
<value>classpath:c.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
If I understand the scenario correctly, you have 3 configs for each environment? Maybe regional, and you switch between the different xml files.
What you should do instead is have just one xml file, that resolves to properties in a properties file.
You have one properties file per environment in your resources folder.
At startup you can then pass a jvm argument to set the "region" or way you identify the environment which will then "fill" your xml where there are placeholders to the values in the properties files.
This site should help you get started.
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
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:
I have the bean definitions below. If I change the placeholderPrefix for the "exposeSystemProperties" bean to "${" and use that in the properties path of the second bean, it works. If I change it to anything but "%{" it doesn't work. I can't use any other string (e.g. "$sys{", "#[", etc.). I'm currently on 3.0.5.RELEASE.
Any thoughts as to why this is? To compound it all, I have a 3rd PropertyPlaceHolderConfigure, so only having two prefixes does not work.
<bean id="exposeSystemProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="placeholderPrefix"><value>$sys{</value></property>
<property name="order" value="10" />
</bean>
<bean id="localFileProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_NEVER" />
<property name="placeholderPrefix" value="%{" />
<property name="placeholderSuffix" value="}" />
<property name="order" value="20" />
<property name="locations">
<array>
<bean class="java.lang.String">
<constructor-arg><value>classpath:properties/$sys{deploy.env}/client.properties</value></constructor-arg>
</bean>
</array>
</property>
</bean>
Since what you need the prefix for is to control environment specific properties, this can be done by using system variables ( instead of a deploy.env property in your example ):
<value>classpath:properties/${ENV_SYSTEM:dev}/client.properties</value>
In this case it will always look under:
<value>classpath:properties/dev/client.properties</value>
by default, unless a ENV_SYSTEM system variable is set. If it is set to "qa", for example, it will automatically look under:
<value>classpath:properties/qa/client.properties</value>
Another approach, in case you are open to "look into the future" a bit, is to use Spring 3.1's PROFILE feature, where beans can be profile specific. For example:
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
This dataSource will only be loaded in case a profile is set to dev:
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.getEnvironment().setActiveProfiles( "dev" );
ctx.load( "classpath:/org/boom/bang/config/xml/*-config.xml" );
ctx.refresh();
I have a web app that uses Spring's Log4jConfigurer class to initialize my Log4J log factory. Basically it initializes Log4J with a config file that is off the class path.
Here is the config:
<bean id="log4jInitializer" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="sbeHome">
<property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<property name="targetMethod" value="initLogging" />
<property name="arguments">
<list>
<value>#{ MyAppHome + '/conf/log4j.xml'}</value>
</list>
</property>
</bean>
However I get this error at application startup:
log4j:WARN No appenders could be found for logger
and tons of Spring application context initialization messages are printed to the console. I think this is because Spring is doing work to initialize my application before it has a chance to initialize my logger. In case it matters, I am using SLF4J on top of Log4J.
Is there some way I can get my Log4jConfigurer to be the first bean initialized? or is there some other way to solve this?
You could configure your Log4j listener in the web.xml instead of the spring-context.xml
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.web.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
So it is up before Spring starts.
Our standalone application required an SMTPAppender where the email config already exists in a spring config file and we didn't want that to be duplicated in the log4j.properties.
I put the following together to add an extra appender using spring.
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<bean factory-method="getRootLogger"
class="org.apache.log4j.Logger" />
</property>
<property name="targetMethod">
<value>addAppender</value>
</property>
<property name="arguments">
<list>
<bean init-method="activateOptions"
class="org.apache.log4j.net.SMTPAppender">
<property name="SMTPHost" ref="mailServer" />
<property name="from" ref="mailFrom" />
<property name="to" ref="mailTo" />
<property name="subject" ref="mailSubject" />
<property value="10" name="bufferSize" />
<property name="layout">
<bean class="org.apache.log4j.PatternLayout">
<constructor-arg>
<value>%d, [%5p] [%t] [%c] - %m%n</value>
</constructor-arg>
</bean>
</property>
<property name="threshold">
<bean class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"
id="org.apache.log4j.Priority.ERROR" />
</property>
</bean>
</list>
</property>
</bean>
We also have a log4j.properties file on the classpath which details our regular FileAppenders.
I realise this may be overkill for what you require :)
Rather than configuring log4j yourself in code, why not just point log4j at your (custom) configuration file's location by adding
-Dlog4j.configuration=.../conf/log4j.xml
to your server's startup properties?
Even better, just move log4j.xml to the default location - on the classpath - and let log4j configure itself automatically.
You can use classpath instead of hardcoded path. It works for me
<bean id="log4jInitializer" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="sbeHome">
<property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<property name="targetMethod" value="initLogging" />
<property name="arguments">
<list>
<value>classpath:/conf/log4j.xml</value>
</list>
</property>
</bean>
If you are using Jetty you can add extra classpaths on a per application basis:
http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading#Adding_Extra_Classpaths_to_Jetty
This will allow you to load your log4 properties in a standard manner (from the classpath:)
in web.xml:
<listener>
<listener-class>org.springframework.web.util.Log4jWebConfigurer</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:${project.artifactId}-log4j.properties</param-value>
</context-param>
in jetty-web.xml:
<Set name="extraClasspath">
<SystemProperty name="config.home" default="."/>/contexts/log4j
</Set>