I currently running into trouble with spring security, I have been following the two last tutorials referenced on the spring security article page
Methods secured with the org.springframework.security.access.annotation.Secured don't seem to trigger any Spring Security logic.
This is my test file:
public class AclServiceTest {
#Autowired
PersonDataOnDemand pdod;
#Autowired
MyAclService aclService;
UserDetailsService uds = new MyUserDetailsService();
#Test
public void testWriteResourceAnnotation(){
Person p0 = pdod.getSpecificPerson(0);
Person p1 = pdod.getSpecificPerson(1);
Assert.isTrue(!p0.getId().equals(p1.getId()));
Resource r = new Resource(p0.getSite(), p0, p0.getPrivateFolder());
authenticatePerson(p0);
securedWriteResource(r);
authenticatePerson(p1);
try{
securedWriteResource(r);
fail();
} catch(Exception e){
}
}
#Secured("ACL_RESOURCE_WRITE")
public void securedWriteResource(Resource r){
return;
}
private void authenticatePerson(Person p){
UserDetails ud = uds.loadUserByUsername(p.getEmail());
SecurityContextHolder.getContext().setAuthentication(new RunAsUserToken("user-"+p.getId(), ud, p.getPassword(), ud.getAuthorities().toArray(new GrantedAuthority[0]), null));
}
}
If added the following lines to my web.xml
<!--Spring security filter-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And this is my security.xml configuration with the beans that are used:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd" >
<beans:bean id="ehCacheBasedAclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
<beans:constructor-arg>
<beans:bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<beans:property name="cacheManager">
<beans:bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
</beans:property>
<beans:property name="cacheName" value="aclCache"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<!--
Partie gestion de la business logic ACL
-->
<global-method-security secured-annotations="enabled" access-decision-manager-ref="businessAccessDecisionManager"/>
<beans:bean id="businessAccessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<beans:property name="allowIfAllAbstainDecisions" value="false"/>
<beans:property name="decisionVoters">
<beans:list>
<beans:ref local="roleVoter"/>
<beans:ref local="aclResourceReadVoter"/>
<beans:ref local="aclResourceWriteVoter"/>
<beans:ref local="aclResourceDeleteVoter"/>
<beans:ref local="aclResourceAdminVoter"/>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
<beans:bean id="aclResourceReadVoter" class="org.springframework.security.acls.AclEntryVoter">
<beans:constructor-arg ref="jdbcMutableAclService"/>
<beans:constructor-arg value="ACL_RESOURCE_READ"/>
<beans:constructor-arg>
<beans:list>
<beans:ref local="administrationPermission"/>
<beans:ref local="readPermission"/>
</beans:list>
</beans:constructor-arg>
<beans:property name="processDomainObjectClass" value="myapp.models.Resource"/>
<beans:property name="internalMethod" value="getRootFolder"/>
</beans:bean>
<beans:bean id="aclResourceWriteVoter" class="org.springframework.security.acls.AclEntryVoter">
<beans:constructor-arg ref="jdbcMutableAclService"/>
<beans:constructor-arg value="ACL_RESOURCE_WRITE"/>
<beans:constructor-arg>
<beans:list>
<beans:ref local="administrationPermission"/>
<beans:ref local="writePermission"/>
</beans:list>
</beans:constructor-arg>
<beans:property name="processDomainObjectClass" value="myapp.models.Resource"/>
<beans:property name="internalMethod" value="getRootFolder"/>
</beans:bean>
<beans:bean id="aclResourceDeleteVoter" class="org.springframework.security.acls.AclEntryVoter">
<beans:constructor-arg ref="jdbcMutableAclService"/>
<beans:constructor-arg value="ACL_RESOURCE_DELETE"/>
<beans:constructor-arg>
<beans:list>
<beans:ref local="administrationPermission"/>
<beans:ref local="deletePermission"/>
</beans:list>
</beans:constructor-arg>
<beans:property name="processDomainObjectClass" value="myapp.models.Resource"/>
<beans:property name="internalMethod" value="getRootFolder"/>
</beans:bean>
<beans:bean id="aclResourceAdminVoter" class="org.springframework.security.acls.AclEntryVoter">
<beans:constructor-arg ref="jdbcMutableAclService"/>
<beans:constructor-arg value="ACL_RESOURCE_ADMIN"/>
<beans:constructor-arg>
<beans:list>
<beans:ref local="administrationPermission"/>
</beans:list>
</beans:constructor-arg>
<beans:property name="processDomainObjectClass" value="myapp.models.Resource"/>
<beans:property name="internalMethod" value="getRootFolder"/>
</beans:bean>
<beans:bean id="administrationPermission" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<beans:property name="staticField" value="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
</beans:bean>
<beans:bean id="readPermission" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<beans:property name="staticField" value="org.springframework.security.acls.domain.BasePermission.READ"/>
</beans:bean>
<beans:bean id="writePermission" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<beans:property name="staticField" value="org.springframework.security.acls.domain.BasePermission.WRITE"/>
</beans:bean>
<beans:bean id="deletePermission" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<beans:property name="staticField" value="org.springframework.security.acls.domain.BasePermission.DELETE"/>
</beans:bean>
The businessAccessDecisionManager bean is created and is given the voters, but the decide method is never called.
Anyone has an idea what's going wrong ?
Thank for your help.
I found out that <global-method-security ../> wasn't inserted in the right xml file. See here for more details. This file happens to be webmvc-config.xml in my Roo project.
That unfolded several problem, I need SpEL for ACL so I finally settled for this config:
<bean class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" id="methodExprHandler">
<property name="permissionEvaluator" ref="aclPermissionEvaluator" />
</bean>
<bean class="org.springframework.security.acls.AclPermissionEvaluator" id="aclPermissionEvaluator">
<constructor-arg ref="jdbcMutableAclService" />
</bean>
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="methodExprHandler"/>
</security:global-method-security>
No voter logic for authorization.
Related
I want to write data to two different files. If my condition is true then write to x file otherwise to y file.
Currently I have a flat file item writer that writes all the data to one file. I want to specify two different files and based on condition data should go to perticular file.
<beans:bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<beans:property name="resource">
<beans:bean class="org.springframework.core.io.FileSystemResource" scope="step">
<beans:constructor-arg value="${process.output.dir}/${output.file.name}_#{jobParameters['run.id']}.${output.file.extension}" type="java.lang.String" />
</beans:bean>
</beans:property>
<beans:property name="lineAggregator">
<beans:bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<beans:property name="delimiter" value="|" />
<beans:property name="fieldExtractor">
<beans:bean class="com.abc.common.batch.item.file.transform.FormattingFieldExtractorDecorator">
<beans:property name="fieldExtractor">
<beans:bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor" >
<beans:property name="names" value="${output.field.names}" />
</beans:bean>
</beans:property>
<beans:property name="defaultDateFormat" value="yyyyMMdd" />
<beans:property name="defaultDecimalFormat" value="############0.00" />
</beans:bean>
</beans:property>
</beans:bean>
</beans:property>
<beans:property name="headerCallback">
<beans:bean class="com.abc.common.batch.item.file.HeaderWriter">
<beans:constructor-arg value="${output.column.names}" />
</beans:bean>
</beans:property>
<beans:property name="footerCallback">
<beans:bean class="com.abc.footer.ItemCountFooterCallback" >
<beans:constructor-arg name="count" ref="itemCount" />
</beans:bean>
</beans:property>
</beans:bean>
ClassifierCompositeItemWriter is the way to go. It uses a Classifier to classify items (this is where you would implement your condition) and multiple item writers (each one will write a specific "class" of items).
You can find an example here.
Hope this helps.
you can definitely do this , you can implement a custom item writer and then based on your conditions you can achieve the task of writing into different files.
But in this case - the condition and handling the file may hamper the performance of your application , you need to be careful with your implementation
Here I am just giving you the layout , you can write the implementation of writing in your file , first write the custom item writer -
https://www.programcreek.com/java-api-examples/index.php?api=org.springframework.batch.item.ItemWriter
And then implement a custom itemWriterListener - in which using the beforeWrite method - you can set a different resource
public class WriteListener implements ItemWriteListener{
#Autowire
ItemWrite writer;
public void beforeWrite(List items) {
condition 1 (){
writer.setResource(1)
}
condition 2 (){
writer.setResource(2)
}
}
public void onWriteError(Exception exception, List items) {
System.out.println("Error occurred when writing items!");
}
public void afterWrite(List items) {
}
}
I have used:
spring mvc-version:4.3.7 postgres:9.5 Tool:STS
I want to update my database. But after entry in the form , when I click submit button- it is shown:
Request processing failed; nested exception is
org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException:
Object of class [com.asha.farmvill.model.Division] with identifier
[0]: optimistic locking failed; nested exception is
org.hibernate.StaleObjectStateException: Row was updated or deleted by
another transaction (or unsaved-value mapping was incorrect) :
[com.asha.farmvill.model.Division#0]
this is my controller:
#RequestMapping("/edit/{id}")
public String editDivision(#PathVariable("id") int id, Model model) {
model.addAttribute("division", this.divisionService.getdivisionById(id));
return "editdivision";
}
#PostMapping(value="/updatedivision")
public String updateEmployee(#ModelAttribute("division")Division division, ModelMap model) {
this.divisionService.updatedivision(division);
return "divisionlist";
}
The DAO is given below:
public void updatedivision(Division p) {
Session session = this.sessionFactory.getCurrentSession();
session.update(p);
System.out.println("Division updated successfully, Division Details=" + p.getName());
}
This is my servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
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.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by #Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<beans:property name="driverClassName" value="org.postgresql.Driver" />
<beans:property name="url"
value="jdbc:postgresql://localhost:5432/firmvilldb" />
<beans:property name="username" value="postgres" />
<beans:property name="password" value="root" />
</beans:bean>
<!-- Hibernate 4 SessionFactory Bean definition -->
<beans:bean id="hibernate5AnnotatedSessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="annotatedClasses">
<beans:list>
<beans:value>com.asha.farmvill.model.Division</beans:value>
</beans:list>
</beans:property>
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect
</beans:prop>
<beans:prop key="hibernate.show_sql">true</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<beans:bean id="divisionDao" class="com.asha.farmvill.dao.DivisionDaoImp">
<beans:property name="sessionFactory" ref="hibernate5AnnotatedSessionFactory" />
</beans:bean>
<beans:bean id="divisionService" class="com.asha.farmvill.service.DivisionServiceImp">
<beans:property name="divisionDao" ref="divisionDao"></beans:property>
</beans:bean>
<context:component-scan base-package="com.asha.farmvill" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<beans:bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<beans:property name="sessionFactory" ref="hibernate5AnnotatedSessionFactory" />
</beans:bean>
</beans:beans>
How can I solve the problem?
Add #Transactional annotation on top of the method like below.
#Transactional
public void updatedivision(Division p) {
Session session = this.sessionFactory.getCurrentSession();
session.update(p);
System.out.println("Division updated successfully, Division Details=" + p.getName());
}
Refer Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
This is my situation:
a Postgres database where I have only Spring Batch tables
an Oracle database where I have business data to read, write and update
I have tried to configure an XA enviroment with Atomikos and it seems to work but honestly I haven't understand what is exactly happening.
Can you check my configuration, please? I'm totally noob in these things...
If I set "hibernate.transaction.jta.platform" to "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform" instead of my class "SpringJtaPlatformAdapter" it seems that the batch doesn't commit on my Postgres DB. Why?
For testing I was using Spring "JpaPagingItemReader". With this reader I get always the exception "java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()" so I copied this solution: solution
Why I need to do this? Is there another JpaReader for XA?
Thanks a lot for your help.
Here my configuration:
spring-batch-core 3.0.7.RELEASE
spring-jdbc and spring-orm 4.0.5.RELEASE
hibernate-entitymanager 5.0.7.Final
Atomikos transactions-jta, transactions-jdbc, transactions-hibernate4 4.0.6
database.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="oracleDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource" />
<property name="uniqueResourceName" value="oracleDS" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="3"/>
<property name="testQuery" value="select * from dual" />
<property name="xaProperties">
<props>
<prop key="URL">${database.oracle.url}</prop>
<prop key="user">${database.oracle.username}</prop>
<prop key="password">${database.oracle.password}</prop>
</props>
</property>
</bean>
<bean id="postgresDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="xaDataSourceClassName" value="org.postgresql.xa.PGXADataSource" />
<property name="uniqueResourceName" value="postgresDS" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="3"/>
<property name="testQuery" value="select * from batch_job_execution" />
<property name="xaProperties">
<props>
<prop key="serverName">localhost</prop>
<prop key="databaseName">postgres</prop>
<prop key="user">${database.postgresql.username}</prop>
<prop key="password">${database.postgresql.password}</prop>
</props>
</property>
</bean>
<bean id="atomikosTransactionService" class="com.atomikos.icatch.config.UserTransactionServiceImp"
init-method="init" destroy-method="shutdownForce">
<constructor-arg>
<props>
<prop key="com.atomikos.icatch.service">com.atomikos.icatch.standalone.UserTransactionServiceFactory</prop>
<prop key="com.atomikos.icatch.tm_unique_name">coriTransactionManager</prop>
</props>
</constructor-arg>
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" depends-on="atomikosTransactionService">
<property name="transactionTimeout" value="300" />
</bean>
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" depends-on="atomikosTransactionService" destroy-method="close">
<property name="forceShutdown" value="true" />
<property name="startupTransactionService" value="false" />
</bean>
<bean id="mainTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
</bean>
<alias name="mainTransactionManager" alias="transactionManager" />
<!-- inject the Atomikos transaction manager into a Spring Hibernate adapter for JTA Platform -->
<bean id="springJtaPlatformAdapter" class="com.mydomain.jta.SpringJtaPlatformAdapter">
<property name="jtaTransactionManager" ref="mainTransactionManager" />
</bean>
<bean id="entityManagerFactoryOracle" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="mainTransactionManager,springJtaPlatformAdapter">
<property name="persistenceXmlLocation" value="classpath:persistence.xml" />
<property name="persistenceUnitName" value="oraclePersistenceUnit" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapterOracle"/>
<property name="dataSource" ref="oracleDataSource" />
<property name="jpaPropertyMap" ref="jpaPropertyMapOracle"></property>
</bean>
<bean id="jpaVendorAdapterOracle" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false"/>
<property name="showSql" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
</bean>
<util:map id="jpaPropertyMapOracle">
<entry key="hibernate.transaction.jta.platform" value="com.mydomain.jta.SpringJtaPlatformAdapter" />
<!-- <entry key="hibernate.transaction.jta.platform" value="com.atomikos.icatch.jta.hibernate4.AtomikosPlatform"/> -->
</util:map>
</beans>
context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="postgresDataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseType" value="POSTGRES" />
<property name="isolationLevelForCreate" value="ISOLATION_DEFAULT"/>
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
</beans>
SpringJtaPlatformAdapter
public class SpringJtaPlatformAdapter extends AbstractJtaPlatform {
private static final long serialVersionUID = 1L;
private static TransactionManager sTransactionManager;
private static UserTransaction sUserTransaction;
#Override
protected TransactionManager locateTransactionManager() {
return sTransactionManager;
}
#Override
protected UserTransaction locateUserTransaction() {
return sUserTransaction;
}
public void setJtaTransactionManager(JtaTransactionManager jtaTransactionManager) {
sTransactionManager = jtaTransactionManager.getTransactionManager();
sUserTransaction = jtaTransactionManager.getUserTransaction();
}
}
Hi I am new to Spring batch and using version 3.0.4.RELEASE for my POC to process a very large flat file as below.
Running the batch in chunk processing mode.
A custom reader delegate with synchronized methods to run the
A custom processor which validates and tranforms the data.
The file format is as below.
COB_DATE HIERARCHY_NODE_ID HIERARCHY_LEVEL MEASURE CURRENCY VALUE
20151009 329715 COSTCENTER TOV_Position_60d USD 21848543.55
20151009 329715 COSTCENTER TOV_Position_90d USD 9040531.7
201510009 329715 COSTCENTER TOV_Ratio_30d 47.0069727152
20151009 329715 COSTCENTER TOV_Ratio_60d 20.2442137613
20151009 329715 COSTCENTER TOV_Ratio_90d 121.7425393354
20151009 329715 COSTCENTER TOV_Turnover_30d USD 259996659.00
I have a validation on the COB_DATE field, and in the processor I am throwing an ValidationException if the format is incorrect.
The problem is all the valid rows before the validation exception was thrown are skipped and unavailable for the writer. In this case row1 and row2.
Here is the job configuration
<beans:import resource="JobContext.xml" />
<!-- Starting point of the feed runner -->
<beans:bean id="feedJobRunner" class="com.ubs.risk.arisk.job.FeedJobRunner" >
<beans:constructor-arg name="jobLauncher" ref="jobLauncher"/>
<beans:constructor-arg name="job" ref="feedJob"/>
<beans:constructor-arg name="feedDao" ref="feedDao"/>
</beans:bean>
<beans:bean id="feedReader" class="com.ubs.risk.arisk.job.batch.FeedReaderDelegate" scope="step">
<beans:constructor-arg>
<beans:bean class="org.springframework.batch.item.file.FlatFileItemReader">
<beans:property name="resource" value="#{jobParameters['inputResource']}"/>
<beans:property name="linesToSkip" value="1"/>
<beans:property name="lineMapper">
<beans:bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<beans:property name="lineTokenizer">
<beans:bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<beans:property name="delimiter" >
<util:constant static-field="org.springframework.batch.item.file.transform.DelimitedLineTokenizer.DELIMITER_TAB" />
</beans:property>
<beans:property name="names" value="#{jobParameters['fieldNames']}"/>
<beans:property name="strict" value="false"/>
</beans:bean>
</beans:property>
<beans:property name="fieldSetMapper" >
<beans:bean class="com.ubs.risk.arisk.job.batch.FeedFieldSetMapper"/>
</beans:property>
</beans:bean>
</beans:property>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="feedProcessor" class="com.ubs.risk.arisk.job.batch.FeedRowProcessor" scope="step" >
<beans:constructor-arg name="feedDao" ref="feedDao" />
<beans:constructor-arg name="stepExecution" value="#{stepExecution}"/>
</beans:bean>
<beans:bean id="feedWriter" class="com.ubs.risk.arisk.job.batch.FeedWriter" scope="step">
<beans:constructor-arg name="dataSource" ref="dataSource" />
</beans:bean>
<job id="feedJob">
<step id="importFeedStep">
<tasklet task-executor="stepExecutor" throttle-limit="10">
<chunk reader="feedReader" processor="feedProcessor" writer="feedWriter" commit-interval="300" skip-limit="100" >
<skippable-exception-classes>
<include class="com.ubs.risk.arisk.job.batch.exception.FeedValidationException"/>
</skippable-exception-classes>
<listeners>
<listener>
<beans:bean class="com.ubs.risk.arisk.job.batch.listener.FeedSkipListener">
<beans:property name="feedDao" ref="feedDao"/>
</beans:bean>
</listener>
</listeners>
</chunk>
</tasklet>
<listeners>
<listener >
<beans:bean id="feedStepListener" class="com.ubs.risk.arisk.job.batch.listener.FeedStepListener">
<beans:constructor-arg name="feedDao" ref="feedDao" />
</beans:bean>
</listener>
</listeners>
</step>
<listeners>
<listener>
<beans:bean id="feedJobListener" class="com.ubs.risk.arisk.job.batch.listener.FeedJobListener">
<beans:constructor-arg name="processDao">
<beans:bean class="com.ubs.risk.arisk.job.batch.dao.JdbcProcessDao">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
</listener>
</listeners>
<validator ref="jobParamValidator"/>
</job>
<beans:bean id="stepExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
<beans:bean id="jobParamValidator" class="com.ubs.risk.arisk.job.batch.validation.JobParameterValidator"/>
Thanks in advance for any suggestions.
I've defined a global formatter for date bindings:
<annotation-driven conversion-service="conversionService" />
<beans:bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<beans:property name="converters">
<beans:list>
...
</beans:list>
</beans:property>
<beans:property name="formatters">
<beans:set>
<beans:bean id="dateFormatter"
class="org.springframework.format.datetime.DateFormatter">
<beans:constructor-arg><beans:value>dd/MM/yyyy</beans:value></beans:constructor-arg>
</beans:bean>
</beans:set>
</beans:property>
</beans:bean>
This is working fine but now I also need that a particular date field can be left blank. How can I configure the formatter (or this very field) to accept null value?
Thanks! :)
You can do this by set lenient in false for DateFormatter
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="org.springframework.format.datetime.DateFormatter">
<constructor-arg>
<value>dd/MM/yyyy</value>
</constructor-arg>
<property name="lenient" value="false"/>
</bean>
</set>
</property>
</bean>