Spring Context File Reuse with Different Properties - spring

I have a fairly straight-forward spring-integration app that polls an external FTP site, processes any found files using some internal business logic and then posts a results file via FTP. Currently when we add a new customer, we copy the XML config file and a corresponding properties file and change a prefix on all bean definitions, bean references and properties. We then add those to an XML config file that simply does imports for all existing customers and their properties:
<property-placeholder location="
file:config/application.properties
,file:config/test-customer1.properties
,file:config/test-customer2.properties
"/>
<import resource="test-customer1-context.xml" />
<import resource="test-customer2-context.xml" />
These xml files are identical except for a unique customer prefix in the bean names. Seems like there must be a way for me to reuse a single XML file (or a Java Config object) with different sets of properties. My first thought was to implement a custom CustomerScope, but I don't see how the scope implementation can know the proper customer.
Any ideas as to how to accomplish this?
As requested, sample of customer context file
<bean id="testClient1RequestQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="${test.client1.in.queue}"/>
</bean>
<bean id="testClient1ResultsQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="${test.client1.results.queue}"/>
</bean>
...

Related

Does Spring support nested properties across multiple properties files?

I'd like to separate properties across multiple files and allow files loaded later to have properties that refer or combine properties defined previously (nested). For example:
application.properties (e.g. dbName=test, dbHost=targethost)
mysql.properties (e.g. jdbcDriver=com.mysql.jdbc.Driver, jdbcUrl=jdbc:mysql://${dbHost:localhost):${dbPort:3306}/${dbName})
And I use declarations such as:
<context:property-placeholder location="classpath:application.properties" order="0" ignore-unresolvable="false"/>
<context:property-placeholder location="classpath*:${dbType:mysql}.properties" order="1" ignore-unresolvable="false"/>
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="driverClass" value="${jdbcDriver}"/>
When I try to create a dataSource in Spring config I get an error saying that ${dbName} or ${jdbcDriver} is unresolved.
For example:
Could not resolve placeholder 'jdbcDriver' in string value "${jdbcDriver}";
Could someone shed light if it is possible to organize properties into different files in this fashion, and if so, how to configure Spring to resolve the properties properly so my bean declaration does not fail?
Thanks in advance.

Where does business processing datasource config go in Spring Batch?

I have been learning the Spring Batch framework to try to put in practice at work through the online documentation as well as the Pro Spring Batch book by Appress. I have a quick question.
Scenario
I want to do a simple test where I read from the database, do some processing, and then write to another database.
Question
I understand that there is a configuration file called launch-context.xml that contains the Job Repository database schema to maintain the state of the jobs and each of the steps for each one of them.
Say that I have a Source Database (A) where I do a read from and a Target Database (B) where I write to.
Maybe I have overlooked it but...
Where do I put the data source information for A and B?
I guess it depends on the answer of #1 but if put it under src/main/resources say for example source-datasource.xml and target-datasource.xml How is Spring going to pick it up and wire it appropriately? In Spring web app development I usually put those types of files under the context-param tag.
You can define these datasources in any spring file of your choosing, so yes:
src/main/resources/db/source-datasource.xml
src/main/resources/db/target-datasource.xml
will do.
Let's say you named your datasource beans as a sourceDataSource and a targetDataSource. The way you tell Spring Batch ( or in this case just Spring ) to use them is through the "import" and "dependency injection".
Importing
You can organize your spring configs the way you fit best, but since you already have launch-context.xml, in order for the above datasources to be visible, you need to import them into launch-context.xml as:
<import resource="classpath:db/source-datasource.xml"/>
<import resource="classpath:db/target-datasource.xml"/>
Injecting / Using
<bean id="sourceReader" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="sourceDataSource" />
<!-- other properties here -->
</beans:bean>
<bean id="targetWriter" class="org.springframework.batch.item.database.JdbcBatchItemWriter">
<property name="dataSource" ref="targetDataSource" />
<!-- other properties here -->
</beans:bean>
where a sourceReader and a targetWriter are the beans you would inject into your step(s).

Spring multiple error messages

I suppose that everybody that uses spring, uses form binding and validation. And you all defined the messages to display on validation errors. I did it with this in my config:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="messages" />
what will happen basically is that it will read messages.properties in my root folder of the project.
But I'd need to put messages in two separate files. Because one part of the app has to be standalone. I tried adding this just after the one above:
<bean id="messageSourceAssistenza"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename = "com.mypackage.other.assistenzamessages.properties"
/>
but it can't resolve those messages at all. How to solve this?
You should be able to use ResourceBundleMessageSource.setBasenames that accepts array of base names:
Set an array of basenames, each
following ResourceBundle conventions:
essentially, a fully-qualified
classpath location. If it doesn't
contain a package qualifier (such as
org.mypackage), it will be resolved
from the classpath root.
The associated resource bundles will
be checked sequentially when resolving
a message code. Note that message
definitions in a previous resource
bundle will override ones in a later
bundle, due to the sequential lookup.
Sample configuration as follows:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages_1</value>
<value>messages_2</value>
...
<value>messages_n</value>
</list>
</property>
</bean>

Good example of Spring Configuration using java.util.prefs or Commons Configuration

One application I'm working on has several URLs and other information that is instance specific. The first pass uses a typical Spring PropertyPlaceholderConfigurer with a properties file:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:application.properties"/>
</bean>
The main issue with this is of course the property file is an artifact that must be checked in, and for starting a new instance would require updating that artifact. For a streamline deployment, I would like to have the ApplicationContext bootstrap itself based on database table(s). I have seen solutions like this forum post, does anyone here know of better tools or is this defacto approach to this problem? I would also like to be able to update/reload the settings at runtime using JMX or other facilities, but having to restart the app after changes to the database would still be a better solution to the current one.
The way we did it was to put some configuration information in the environment and then pull the relevant info from there.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="searchSystemEnvironment" value="true" />
</bean>
If configuration changes then the app will need to be restarted. Can also put all the different configurations into the environment and nest the variables like the following:
<bean id="db" class="org.DataSource"
p:databaseServer="${${MODE}_DBSERVER}"
p:databaseName="${${MODE}_DBNAME}" />
where $MODE = dev, qa, etc.

external config based on context path

I would like to deploy multiple independent copies of a particular web-app on the same tomcat server under different context paths. Each web-app will need different configuration settings (database name, password, etc), but I would like to keep the wars exactly identical.
My plan was to have the app figure out its context path on startup, then read a specific .properties file outside of tomcat identified by the context path. For example, if a war was deployed to {tomcat path}/webapps/pineapple, then I would want to read /config/pineapple.properties
I've been trying to find a way to inject an instance of ServletContext via spring (3), but all the advice I've seen so far use the deprecated ServletContextFactoryBean.
Is there a better way to get the context path injected or better way to load external files based on the context path?
With the help of ServletContextAttributeFactoryBean and Spring EL, you can reference ServletContext init parameters (<context-param> in web.xml) like that:
#{contextAttributes.myKey}
This allows you to use PropertyPlaceHolderConfigurer and load property files from arbitrary, user-defined locations:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="#{contextParameters.APP_HOME}/conf/app.properties"/>
</bean>
The corresponding definition of the ServletContext init parameter in Tomcat's context.xml:
<Parameter name="APP_HOME" value="file:/test" override="false"/>
Or in your app's web.xml:
<context-param>
<param-name>APP_HOME</param-name>
<param-value>file:/test</param-value>
</context-param>
This should be the solution.
<bean name="envConfig" class="EnvironmentConfiguration">
<property name="locations">
<list>
<value>file:///#{servletContext.contextPath}.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
Extend Propertyplaceholderconfigurer to use DB to pick up the values. Example here
Load the actual values of the settings (database name, password etc) to the db as part of seed data
When your web-app's app ctx is being initialized, the properties are resolved from the DB
This is the approach we have been following and works great. If you can switch to Spring 3.1 then it has support for Environment Profiles which may be useful for you.

Resources