How do I set Spring Batch jobParameters in xml? - java

This job.xml works great when using parameters.
<beans>
<bean id="testFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<property name="resource" value="#{jobParameters['paramFileOuput']}"/>
<property name="shouldDeleteIfExists" value="true" />
<property name="lineAggregator">
...
</property>
</bean>
</beans>
But what if I don't want to use job parameters but instead use Spring profiles...
<beans>
<bean id="testFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter" scope="step">
<property name="resource" ref="testFileOutput"/>
<property name="shouldDeleteIfExists" value="true" />
<property name="lineAggregator">
....
</property>
</bean>
<beans profile="dev">
<bean id="testFileOutput" class="java.lang.String">
<constructor-arg type="java.lang.String" value="file:c:/temp/testfile.txt" />
</bean>
</beans>
<beans profile="prod">
<bean id="testFileOutput" class="java.lang.String">
<constructor-arg type="java.lang.String" value="file:pathtoprod.txt" />
</bean>
</beans>
</beans>
This appears to work. But this just doesn't feel right. Is there a way to set the parameter in the xml using profiles?
Something along these lines (this obviously doesn't work)
<beans profile="dev">
<setJobParameter key="testFileOuput" value="file:c:/temp/testfile.txt" />
</beans>
I can use any version of spring or springbatch.

How about this:
public FileOutput {
private String filename;
...
}
and
<beans profile="dev">
<bean id="testFileOutput" class="FileOutput " c:filename="file:pathtoprod.txt">
</beans>
Unluckily, you cant use p/c namespace with java.lang.String, thats why you need a little helper class. Anyway, the solution would shrink your context.xml a few lines and looks more similar to your desired solution.

Related

Spring Webflow's _flowId deprecated

I've been assigned a situation to migrate a spring-webflow-1.0 project to spring-webflow-2.4.
Also I've been asked to maintain the original _flowId identification i.e. /flows.htm?_flowId=booksearch
In order to do so, I've put together the following flow-definition.xml that is loaded by spring. It uses the WebFlow1FlowUrlHandler I've seen mentioned in a couple of places to allow you to maintain WebFlow1 based url matching but none of the following urls are finding anything
http://localhost:8080/SpringWebFlowExamples/flows.html?_flowId=booksearch
http://localhost:8080/SpringWebFlowExamples/flows.html?_flowId=pensionclaims
The definitions for booksearch-flow.xml and pensionclaims-flow.xml are located in webapps/flows but to no avail?
Would anyone have any ideas?
Thanks,
Mark.
<?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:flow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<property name="flowRegistry" ref="flowRegistry"/>
</bean>
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor"/>
</bean>
<flow:flow-executor id="flowExecutor" flow-registry="flowRegistry"/>
<flow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<flow:flow-location-pattern value="/flows/*-flow.xml"/>
</flow:flow-registry>
<flow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator"
development="true"/>
<bean id="internalResourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
<property name="viewResolvers" ref="internalResourceViewResolver"/>
<property name="useSpringBeanBinding" value="true"/>
</bean>
<bean name="/flows.html" class="org.springframework.webflow.mvc.servlet.FlowController">
<property name="flowExecutor" ref="flowExecutor"/>
<property name="flowUrlHandler">
<bean class="org.springframework.webflow.context.servlet.WebFlow1FlowUrlHandler"/>
</property>
</bean>

Spring: MethodInvokingFactoryBean calls wrong method

I need to do the following Java line in XML:
usersConnectionRepository.setConnectionSignUp(new AccountConnectionSignUp());
So I did this:
<bean id="usersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
scope="singleton">
<constructor-arg ref="dataSource" />
<constructor-arg ref="connectionFactoryLocator" />
<constructor-arg ref="textEncryptor" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="accountConnectionSignUp" class="edu.kit.tm.cm.ksc.config.AccountConnectionSignUp" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="usersConnectionRepository" />
</property>
<property name="targetMethod">
<value>setConnectionSignUp</value>
</property>
<property name="arguments">
<list>
<ref local="accountConnectionSignUp" />
</list>
</property>
</bean>
The error occurs when the method is supposed to be called.
java.lang.NoSuchMethodException: com.sun.proxy.$Proxy12.setConnectionSignUp(edu.kit.tm.cm.ksc.config.AccountConnectionSignUp)
As you can see above, it's totally searching in the wrong package, and I have no idea why.
I have no idea how to debug this further. I'm inexperienced with Spring and it's XML-Notation. I hope someone can help me. Thank you.
UPDATE
As requested, the complete social.xml. Although, I do not think it is needed to solve this.
<?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:facebook="http://www.springframework.org/schema/social/facebook"
xmlns:twitter="http://www.springframework.org/schema/social/twitter"
xmlns:social="http://www.springframework.org/schema/social" xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/social/facebook http://www.springframework.org/schema/social/spring-social-facebook.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/social/twitter http://www.springframework.org/schema/social/spring-social-twitter.xsd
http://www.springframework.org/schema/social http://www.springframework.org/schema/social/spring-social.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<context:property-placeholder
location="classpath:/edu/kit/tm/cm/ksc/config/application.properties" />
<bean id="connectionFactoryLocator"
class="org.springframework.social.connect.support.ConnectionFactoryRegistry"
scope="singleton">
<property name="connectionFactories">
<list>
<bean
class="org.springframework.social.twitter.connect.TwitterConnectionFactory">
<constructor-arg value="${twitter.consumerKey}" />
<constructor-arg value="${twitter.consumerSecret}" />
</bean>
<bean
class="org.springframework.social.facebook.connect.FacebookConnectionFactory">
<constructor-arg value="${facebook.clientId}" />
<constructor-arg value="${facebook.clientSecret}" />
</bean>
</list>
</property>
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="usersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
scope="singleton">
<constructor-arg ref="dataSource" />
<constructor-arg ref="connectionFactoryLocator" />
<constructor-arg ref="textEncryptor" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="accountConnectionSignUp" class="edu.kit.tm.cm.ksc.config.AccountConnectionSignUp" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="usersConnectionRepository" />
</property>
<property name="targetMethod">
<value>setConnectionSignUp</value>
</property>
<property name="arguments">
<list>
<ref local="accountConnectionSignUp" />
</list>
</property>
</bean>
<bean id="connectionRepository" factory-method="createConnectionRepository"
factory-bean="usersConnectionRepository" scope="request">
<constructor-arg value="#{request.userPrincipal.name}" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="org.springframework.social.connect.web.ConnectController">
<!-- relies on by-type autowiring for the constructor-args -->
<!-- <constructor-arg ref="connectionFactoryLocator" /> -->
<!-- <constructor-arg ref="connectionRepository" /> -->
</bean>
<bean id="SimpleSignInAdapter" class="edu.kit.tm.cm.ksc.config.SimpleSignInAdapter" />
<bean class="org.springframework.social.connect.web.ProviderSignInController">
<!-- relies on by-type autowiring for the constructor-args -->
<constructor-arg ref="SimpleSignInAdapter" />
</bean>
Update 2
We wrote the Java-examples of the Spring-Social-Documentation to XML. In this case for the ProviderSigninControllers dependencies. Unfortunately there are no XML examples given in this case.
The simple solution is to change proxy-target-class to true in your usersConnectionRepository bean definition and add CGLIB to your class path.
If you don't need the proxying, remove it completely.
Explanation:
First, with this bean declaration
<bean id="usersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
scope="singleton">
<constructor-arg ref="dataSource" />
<constructor-arg ref="connectionFactoryLocator" />
<constructor-arg ref="textEncryptor" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
Spring is creating a bean of type JdbcUsersConnectionRepository and wrapping it in a JDK proxy (since proxy-target-class is false). The serious shortcoming of JDK proxies, is that they only sub type interfaces.
In other words, Spring will see that the JdbcUsersConnectionRepository class implements the UsersConnectionRepository interface and use that when generating the Proxy. As the javadoc says
A proxy class extends java.lang.reflect.Proxy.
A proxy class implements exactly the interfaces specified at its creation, in the
same order.
So the generated proxy will be of type Proxy and UsersConnectionRepository.
This won't be an issue for MethodInvokingFactoryBean because it stores the reference in a field of type Object. However, when MethodInvokingFactoryBean tries to resolve the Method to invoke, it uses the target object's Class instance, ie. object.getClass(). Since the target object is actually of type Proxy, or com.sun.proxy.$Proxy12 to be exact, it does not have a JdbcUsersConnectionRepository#setConnectionSignUp method and that causes a NoSuchMethodException.

How to set the resource of MultiResourceItemWriter dynamically from ItemReader in Spring Batch

The batch job is:
Reading from a csv file
Create an xml file for every record(line) in the csv with name Patent.ID.xml(where ID is a field in the csv and Patent is my model class), example: 1.xml, 2.xml
The problem is that I can't find a way to dynamically set the file-name to each ID from the csv file
Here is my configuration:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch"xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<!-- My beans -->
<bean id="patent" class="com.example.model.Patent" scope="prototype" />
<bean id="xmlsuffix" class="com.example.filename.PatentFileSuffixCreator"/>
<bean id="noInputException" class="com.example.listener.NoWorkFoundStepExecutionListener"/>
<batch:job id="csvtoxml">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="fileReader" writer="multiResourceItemWriter" commit-interval="1">
</batch:chunk>
<batch:listeners>
<batch:listener ref="noInputException"/>
</batch:listeners>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="multiResourceItemWriter"
class="org.springframework.batch.item.file.MultiResourceItemWriter">
<property name="resource" value="file:xml/#{patent.ID}" />
<property name="delegate" ref="XMLwriter"/>
<property name="itemCountLimitPerResource" value="1"/>
<property name="resourceSuffixCreator" ref="xmlsuffix"/>
</bean>
<bean id="XMLwriter"
class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="marshaller" ref="patentUnmarshaller" />
<property name="rootTagName" value="Patents" />
</bean>
<bean id="patentUnmarshaller"
class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<map>
<entry key="Patent"
value="com.example.model.Patent" />
</map>
</property>
</bean>
<bean id="fileReader"
class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="lineMapper" ref="lineMapper"/>
<property name="resource" value="file:dbbrev_sample_m.csv"/>
<property name="linesToSkip" value="1"/>
</bean>
<bean id="lineMapper"
class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="fieldSetMapper" ref="fieldSetMapper"/>
<property name="lineTokenizer" ref="lineTokenizer"/>
</bean>
<bean id="lineTokenizer"
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value=","/>
<property name="names" value="ID,TYPE,NOPUBLICATION" />
</bean>
<bean id="fieldSetMapper"
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="targetType" value="com.example.model.Patent"/>
</bean>
</beans>
Create a ItemWriteListener, inject the multiResourceItemWriter bean and bound the listener to your step.
In ItemWriteListener.beforeWrite() create a new ResourceSuffixCreator object using the item your are going to write as base to create resource extension (suffix).
MultiResourceItemWriter.resource probably need to be changed as file:xml/ because 1.xml, 2.xml and so on will be appended using custom ResourceSuffixCreator dinamically created for every item you are writing.
This solution is dirty and (probably) works due to commit-interval=1; if you change my answer (probably) will fail.
I hope I was clear, English is not my native language.
I couldn't find a way to make Luca's solution to work, so I extended MultiResourceItemWriter this way:
public class MyMultipleItemWriter extends MultiResourceItemWriter<MyBeanXml>
{
#Override
public void write(List<? extends MyBeanXml> items) throws Exception
{
this.setResourceSuffixCreator(new MySuffixCreator(items.get(0).getField()));
super.write(items);
}
}

How do I use an optional properties file in a Spring configuration file?

I am using an XML configuration file that is loaded into my Java application using ApplicationContext.
The XML configuration file resolves its properties by reading from several property files using PropertyPlaceholderConfigurer.
I want to make each property file optional. I thought that this is done by setting ignoreUnresolsvablePlaceholders to true, however I am getting the following exception when I run the application (db-default.properties exists but db.properties does not):
Exception in thread "main" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [db.properties] cannot be opened because it does not exist
This is what my XML configuration looks like:
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="placeholder-configurer-1" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
<bean id="placeholder-configurer-2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="2"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="location" value="classpath:/db-default.properties"/>
</bean>
<bean id="placeholder-configurer-3" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="3"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:/db.properties</value>
</list>
</property>
</bean>
<bean id="MyDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
What do I need to do to make db.properties an optional property file?
<property name="ignoreResourceNotFound" value="true"/>
spring blog article
api docs
This should work too:
<context:property-placeholder ignore-resource-not-found="true" location="classpath:your.properties" ... />

Why does Spring 3.x ignore certain placeholderPrefixes for PropertyPlaceholderConfigurer?

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();

Categories