Overriding System property in Spring PropertyPlaceHolderConfigurer for integration testing - spring

I'm using a System property to define the location for an environment-specific properties file. However, I would like to override that value to something different for integration tests.
Here's my production spring setup. I'm using a custom PropertyPlaceholderConfigurer to resolve some encrypted property file values, but that's not important here:
<-- Spring configuration in file service-spring-beans.xml -->
<bean class="com.mycompany.MyPropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:properties/${MY_ENVIRONMENT}/${MY_ENVIRONMENT}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="false"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
At runtime, we define the value of MY_ENVIRONMENT as a Java system property. This all works as expected. However, for integration tests, I would like to define MY_ENVIRONMENT as "inttest", so the integration-test specific property file properties/inttest/inttest.properties is loaded.
I've tried to use a spring context loaded by the integration-test to set up a String bean with the id MY_ENVIRONMENT:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="com.mycompany.myclasses"/>
<bean class="java.lang.String" id="MY_ENVIRONMENT">
<constructor-arg value="inttest"/>
</bean>
<!-- this imports the production spring context -->
<import resource="classpath:service-spring-beans.xml"/>
</beans>
However, the value of MY_ENVIRONMENT is not resolved , and I get this error when running the integration tests.
Caused by:
org.springframework.beans.factory.BeanInitializationException: Could
not load properties; nested exception is
java.io.FileNotFoundException: class path resource
[properties/${MY_ENVIRONMENT}/${MY_ENVIRONMENT}.properties] cannot
be opened because it does not exist
How can I override MY_ENVIRONMENT at inttest time without passing a System property to the JVM?

Since I was using the maven surefire plugin to run integration tests, the simplest solution turned out to be setting the system property using the surefire plugin configuration, like this:
<configuration>
<systemPropertyVariables>
<MY_ENVIRONMENT>inttest</MY_ENVIRONMENT>
</systemPropertyVariables>
<!-- ... -->
<configuration>

Since you don't want to use profiles, you can create multiple contexts and just include the proper context files for what you want. So for integration testing you have have your application-context.xml and an integration-property-context.xml file while, in the prod environment you would include the application-context.xml with a production-property-context.xml file. I've seen this approach used heavily to switch datasources between dev and prod where dev would be a BasicDataSource implementation and the prod environment references a JDNI DataSource.
This approach will help you avoid ActiveProfiles, but you run in to the problem of managing duplicate beans possibly which ActiveProfiles really simplified down.

you could look at overriding your whole property placeholder implementation using active profiles. that is, the default (no profile) launches your property placeholder, but a test profile (e.g. 'test') could create a new test bean for the property placeholder.
one of the challenges with property placeholder is that its loaded at the very early stages of the application context startup, so a normal override bean may not work.

Related

Share a Property between two blueprints

I am trying to define a property in blueprint 1 and use it as the default property in blueprint 2.
Both blueprints will be loaded into the same Karaf OSGI container but exist in different bundles. The manifests are already set up with necessary dependency info.
Here is my Blueprint1 that defines the property i want to use again in another blueprint:
<blueprint
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0
https://osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!-- Properties for this blueprint -->
<osgix:cm-properties id="sharedProperties" persistent-id="com.foo.project" update-strategy="reload"
xmlns="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0">
<default-properties>
<property name="shared-property" value="value"/>
</default-properties>
</osgix:cm-properties>
<bean id="myBean"
class="com.foo.MyClass">
<property name="setting" value="{{shared-property}}"/>
</bean>
And my other Blueprint which is trying to use "my-property" defined above:
<blueprint
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
xmlns:ctx="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0
https://osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!-- Load the properties from Blueprint1. Intent is to use property defined over there, here. -->
<osgix:cm-properties id="sharedProperties" persistent-id="com.foo.project"/>
<!-- Make properties defined in sharedProperties available using ${} syntax-->
<ctx:property-placeholder properties-ref="sharedProperties" />
<!-- Properties for this blueprint -->
<property-placeholder persistent-id="com.foo.project.blueprint2" update-strategy="reload"
xmlns="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
placeholder-prefix="{{{"
placeholder-suffix="}}}">
<default-properties>
<property name="my-property" value="${shared-property}"/>
</default-properties>
</property-placeholder>
<bean id="myBean"
class="com.foo.MyClass">
<property name="setting" value="{{{my-property}}}"/>
</bean>
Ive got a feeling(maybe just hope) this is close to correct but off by a little. If it is correct my issue may be with a missing namespace handler.
By using the "default-properties" tag you are redefining the value. Remove the from the second project. Also, I recommend removing default-properties from both and utilize an externalized cfg file so you do not have a race condition at start-up.
Remove this:
<default-properties>
<property name="my-property" value="${shared-property}"/>
</default-properties>

Issues while running JUnit on maven project using Spring JTA

I have a Spring + Hibernate project deployed on JBoss 5. It was using Ant earlier and recently I have modified the project structure to use Maven 2. But all the code and config files are the same.
I am using Spring JTA for transaction management in my project. I am able to perfecly buid the project and deploy it on JBoss. But when I try to execute the JUnit tests it gives me the following error:
java.lang.IllegalStateException: No JTA UserTransaction available - specify either 'userTransaction' or 'userTransactionName' or 'transactionManager' or 'transactionManagerName'
at org.springframework.transaction.jta.JtaTransactionManager.checkUserTransactionAndTransactionManager(JtaTransactionManager.java:473)
at org.springframework.transaction.jta.JtaTransactionManager.afterPropertiesSet(JtaTransactionManager.java:413)
The wierd thing is that this was working perfeclty fine when I was using Ant. It started coming after the migration to maven.
This is the entry in spring-jpa-conf.xml file:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
</bean>
And this is the Junit code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring-conf.xml" })
#Transactional(value = "transactionManager")
public abstract class LocalUsersTestBase {
#PersistenceContext(unitName="books-lemf")
protected EntityManager entityManager;
protected void getUsersDetails(List<Users> out) {
..........
}
I also tried to add a property in the config file entry as shown here:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
<property name="userTransactionName" value="java:/TransactionManager"></property>
</bean>
But it gives the error:
Caused by: org.springframework.transaction.TransactionSystemException: JTA UserTransaction is not available at JNDI location [java:/TransactionManager]; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at org.springframework.transaction.jta.JtaTransactionManager.lookupUserTransaction(JtaTransactionManager.java:548)
at org.springframework.transaction.jta.JtaTransactionManager.initUserTransactionAndTransactionManager(JtaTransactionManager.java:425)
at org.springframework.transaction.jta.JtaTransactionManager.afterPropertiesSet(JtaTransactionManager.java:412)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1460)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1400)
... 37 more
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:325)
Please help.
Thanks!!
This is my complete spring config file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
</bean>
<jee:jndi-lookup id="books-pu"
jndi-name="java:/books-emf"
cache="true"
lookup-on-startup="false"
proxy-interface="javax.persistence.EntityManagerFactory"
/>
<jee:jndi-lookup id="booksDataSource" jndi-name="java:/books-ds"/>
</beans>
Firsty, from spring reference documentation
If you use JTA in a Java EE container then you use a container
DataSource
, obtained through JNDI,
in conjunction with Spring’s
JtaTransactionManager
. This is what the JTA and JNDI lookup version
would look like:
The
JtaTransactionManager
does not need to know about the
DataSource
, or any other specific
resources, because it uses the container’s global transaction management infrastructure
then when you run the tests you need to a environment container managed to get a JTA User Transaction. In addition can you share with us the complete spring configuration

How to locate the properties file in the spring context configuration file

I am using spring web mvc project, and I put all the spring related files under WEB-INF\spring, including a ormlite.xml and a jdbc.properties.
Now I want to locate the jdbc.properties file in the ormlite.xml,Like this:
<context:property-placeholder location="/WEB-INF/spring/jdbc.properties"/>
But when I run the application ,it will told me that :
Could not load properties
It can not find the properties file.
What is the problem?
From Spring forum:
The problem is that /WEB-INF isn't accessible as it isn't in the root
of the path, you must use the same path as you use in your test case
(include the src/main/webapp part but that will break your application
from running).
I suggest you move the jdbc.properties to the src/main/resources
directory and simply use classpath: prefix to load the properties.
Code:
<context:property-placeholder location="classpath:jdbc.properties"/>
The code above assumes they are on the root of the classpath (which is
where they are when they are in src/main/resources).
I hope this can help someone else.
I had the same problem - property files outside the classpath.
My solution:
First define a properties bean:
<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location">
<value>/WEB-INF/your.properties</value>
</property>
</bean>
Then reference it in the property-placeholder:
<context:property-placeholder properties-ref="configProperties" />
Works perfectly well for me!
Instead of:
<context:property-placeholder location="/WEB-INF/spring/jdbc.properties"/>
Use:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/spring/jdbc.properties"/>
And your properties will be available in Spring file, and don't forget to add a namespace: xmlns:p="http://www.springframework.org/schema/p"
I think you're missing the prefix to instruct Spring how to attempt to load the properties. I think your definition needs to be:
<context:property-placeholder location="file:/WEB-INF/spring/jdbc.properties"/>
Note the addition of the file: prefix.

Weird behaviour of import tag in spring's configuration file

I am working on the spring spring-3.2.2. I have created two java projects in eclipse.
SpringTest
Testclasspath
SpringTest project is having the below beans.xml in which the one bean is defined.
<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="helloWorld" class="com.spring.HelloWorld" init-method="testUpdate" scope="prototype">
<property name="message" value="Hello World!"/>
</bean>
</beans>
I have created the jar springtest.jar of the project SpringTest and it is been added in the classpath of the project Testclasspath. Bean configuration file for the Testclasspath project is talentacquisition.xml and it is importing the beans.xml file of the Springtest project. Please find the below content of talentacquisition.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">
<import resource="Beans.xml"/>
<bean id="juggler" class="com.springaction.springidol.Juggler" />
</beans>
I am confused with behavior of the import tag in the talentacquisition.xml How it is able to locate the Beans.xml which is present in the jar (springtest.jar) in the classpath and able to load the beans? Why spring is not giving any error ? Don't I have to modify the import tag in the talentacqusition.xml to following
<import resource="classpath:Beans.xml"/>
If import is able to locate the file Beans.xml , then when should we use classpath: and classpath* :?
ResourceLoaders are responsible for how Spring loads the resource. From the reference manual
The location path or paths supplied to an ApplicationContext
constructor are actually resource strings, and in simple form are
treated appropriately to the specific context implementation.
ClassPathXmlApplicationContext treats a simple location path as a
classpath location. You can also use location paths (resource strings)
with special prefixes to force loading of definitions from the
classpath or a URL, regardless of the actual context type.
The ClassPathXmlApplicationContext you're instantiating "treats a simple location path as a classpath location", i.e. it treats "Beans.xml" as "classpath:Beans.xml". Similarly, FileSystemXmlApplicationContext would treat "Beans.xml" as "file:Beans.xml".
Section 6.7 of the manual has more details too.

Tomcat Context-Params ignored in Spring webapp when using PropertyPlaceholder

I was previously using, the now deprecated, class org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer to load a properties file from the server's filesystem. I had the following bean definied:
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="locations" value="${config}"/>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="searchContextAttributes" value="true"/>
<property name="contextOverride" value="false"/>
<property name="ignoreResourceNotFound" value="true"/>
<property name="searchSystemEnvironment" value="false"/>
</bean>
The config is an argument that is passed when starting Tomcat, i.e.
-Dconfig=/path/to/application.properties
For the webapp I also have a context file:
<Context docBase="/path/to/application.war">
<Parameter name="host" value="localhost" override="false"/>
<Parameter name="port" value="8080" override="false"/>
</Context>
If the .properties file, specified by the -Dconfig argument, contains the property that some other bean references then the value from the .properties file is used, otherwise the value from the the context xml file is used.
This allowed me to have a set of default properties deployed with the WAR and if required, I was able to specify a .properties file to override particular values.
Now, I'm updating to use the new property abstractions in Spring 3.1 but I can't seem to figure out what the equivalent approach to this is?
I have the same context file and war deployed in the same way, and I now have the following in the application:
<context:property-placeholder
location="${config}"
system-properties-mode="OVERRIDE"
ignore-resource-not-found="true"
ignore-unresolvable="true"/>
This finds and uses the properties from the properties file, BUT it does not use the values from the context XML file.
How do I get my application to use the context params when using this new property-placeholder?
Thanks.
To summarise the problem is that the Context Parameters from the servlet context file were not being used to resolve placeholders when using the new Property Placeholder namespace introduced in Spring 3.1.
I have figured out a solution, with the following
<context:property-placeholder location="${config}" local-override="true" ignore-resource-not-found="true"/>
I can specify one or more *.properties files on the local filesystem using a JVM arg, eg:
-Dconfig=/path/app.properties
If a placeholder property cannot be resolved after checking the app.properties file then the Servlet Context Parameters are checked.
This allows me to have default values using context params in a the web.xml and where I need to I can override these values by specifying the location of *.properties files using the config JVM arg.
The key to getting it to work this way was to include local-override="true", which is false by default. I'm not fully sure that it makes sense, since the description for that attribute is:
Specifies whether local properties override properties from files. Default
is "false": Properties from files override local defaults.
If the same property key exists in the app.properties and the web.xml the value from the app.properties is used.
Spring uses a default property file unless the user-based property file is defined. If you want to control .properties file, please follow the instructions posted here.
If you want to take advantage of application.properties there are two ways to do it.
<!-- allows for ${} replacement in the spring xml configuration from the
system.properties file on the classpath -->
<util:properties id="appProperties" location="classpath:application.properties"/>
<context:property-placeholder location="classpath:application.properties"/>
util tag lets you to use a Property class to read the properties across your application. For example:
#Autowired
public MyPropertyReader(Properties appProperties) {
String prop1 = appProperties.getProperty("my.address");
String prop2 = appProperties.getProperty("my.version");
}
If you want to use the values within your context file use the context:property-placeholder tag. Then you can use your values as
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" p:brokerURL="${jms.primary.server}"/>
where for example jms.primary.server=172.168.10.18:6161 in application.properties.

Resources