Spring batch-Circular reference error while using Java Beans but the application runs properly by using xml beans - spring

Bean "delegateItemWriter" and "itemWriter" have cyclic calls to each other.
When I comment xml bean(which runs fine) and run by using java beans it gives me error as "Requested bean is currently in creation".
Can anyone help why this is happening with java beans only and not for xml beans?
Any help would be appreciated.
Batch Job defination:
<job id="fileToFileWithHeaderFooterJob" job-repository="jobRepository">
<step id="step">
<tasklet>
<chunk reader="itemReader" processor="itemProcessor" writer="itemWriter"
commit-interval="2">
<streams>
<stream ref="delegateItemWriter" />
</streams>
</chunk>
<listeners>
<listener ref="itemWriter" />
</listeners>
</tasklet>
</step>
</job>
If I comment below xml bean(delegateItemWriter) my application breaks and gives me error as Error creating bean with name 'delegateItemWriter': Requested bean is currently in creation: Is there an unresolvable circular reference?
<beans:bean id="delegateItemWriter"
class="org.springframework.batch.item.file.FlatFileItemWriter">
<beans:property name="resource" ref="outputFileResource" />
<beans:property name="shouldDeleteIfExists" value="true" />
<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="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<beans:property name="names"
value="newId,newName,newDate,newParty,newPrice" />
</beans:bean>
</beans:property>
</beans:bean>
</beans:property>
<beans:property name="headerCallback" ref="headerCallback" />
<beans:property name="footerCallback" ref="itemWriter" />
</beans:bean>
Java Config:
#Bean
public FlatFileItemReader<Object> itemReader() {
FlatFileItemReader<Object> itemReader = new FlatFileItemReader<>();
DefaultLineMapper<Object> lineMapper = new DefaultLineMapper<>();
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setNames(new String[] {"dealId", "price", "name", "party", "Date"});
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(new TradeDataFieldSetMapper());
itemReader.setResource(new ClassPathResource("inputData.txt"));
itemReader.setLineMapper(lineMapper);
return itemReader;
}
#Bean
public SimpleItemProcessor itemProcessor() {
return new SimpleItemProcessor();
}
#Bean
public FileToFileFooterCallback itemWriter(final ItemWriter<TradeDataOutput> delegetItemWriter) {
FileToFileFooterCallback footerCallback = new FileToFileFooterCallback();
footerCallback.setDelegate(delegetItemWriter);
return footerCallback;
}
#Bean
public FlatFileItemWriter<TradeDataOutput> delegateItemWriter(final FileToFileFooterCallback itemWriter) {
FlatFileItemWriter<TradeDataOutput> writer = new FlatFileItemWriter<>();
LineAggregator<TradeDataOutput> delimitedLineAggregator = new DelimitedLineAggregator<>();
FieldExtractor<TradeDataOutput> beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<>();
String[] names = {"newID", "newName", "newDate", "newParty", "newPrice"};
((BeanWrapperFieldExtractor<TradeDataOutput>) beanWrapperFieldExtractor).setNames(names);
((DelimitedLineAggregator<TradeDataOutput>) delimitedLineAggregator).setDelimiter(",");
((DelimitedLineAggregator<TradeDataOutput>) delimitedLineAggregator).setFieldExtractor(beanWrapperFieldExtractor);
writer.setLineAggregator(delimitedLineAggregator);
writer.setResource(new FileSystemResource(
"batch/filetoFileWithHeaderFooterOutputFile.data"));
writer.setShouldDeleteIfExists(true);
writer.setHeaderCallback(new FileToFileHeaderCallback());
writer.setFooterCallback((FlatFileHeaderCallback) itemWriter);
return writer;
}

Related

Spring Batch: Footer callback of multiple file writer not giving exact count

If item writers are writing 2 records to file A and 1 record in file B then Trailer count of both the files(A & B) are 3.
I have a reader, processor and ClassifierCompositeItemWriter. In classifier i have two item writers those are giving valid outputs but the footer callback is not proper. in both the file trailer count is same though the record counts are different.
<batch:job id="abc-job" parent="xyzJob">
<batch:step id="inputfile">
<batch:tasklet>
<batch:chunk reader="itemReader" processor="itemProcessor" writer="itemWriter" commit-interval="1000" >
<batch:streams>
<batch:stream ref="AFileWriter"/>
<batch:stream ref="BFileWriter"/>
</batch:streams>
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<beans:bean id="itemWriter" class="org.springframework.batch.item.support.ClassifierCompositeItemWriter">
<beans:property name="classifier" ref="classifier" />
</beans:bean>
<beans:bean id="classifier" class="org.springframework.batch.classify.BackToBackPatternClassifier">
<beans:property name="routerDelegate">
<beans:bean class="com.abc.classifier.MyClassifier" />
</beans:property>
<beans:property name="matcherMap">
<beans:map>
<beans:entry key="A" value-ref="AFileWriter" />
<beans:entry key="B" value-ref="BFileWriter" />
</beans:map>
</beans:property>
</beans:bean>
<beans:bean id="1FileWriter" parent="parentItemWriter1">
<beans:property name="name" value="AFileWriter"/>
<beans:property name="resource" ref="AFile"/>
</beans:bean>
<beans:bean id="2FileWriter" parent="parentItemWriter2">
<beans:property name="name" value="BFileWriter"/>
<beans:property name="resource" ref="BFile"/>
</beans:bean>
Footer callback-
public class ItemCountFooterCallback implements FlatFileFooterCallback
{
private AtomicInteger count;
public ItemCountFooterCallback(final AtomicInteger count)
{
this.count = count;
}
public void writeFooter(final Writer writer) throws IOException
{
writer.append("Trailer " + this.count.toString());
}
}
I expect the output of A and B file's trailer record to be exact number rows of that particular file.
footerCallback is registered at the step level, hence it will use the write.count of the step, which is the total count of written items (3 in your case).
What you can do is to have a write count for each writer (writer1.count and writer2.count for example) and set a footer callback on each writer (not at the step level). Each footer callback should write the item count of the writer it is attached to.

Spring configuration split between xml resource and Java configuration

I am trying to mix both xml and Java Configuration.
I have a spring-security.xml resource that I import in my application boot.
Say this was a part of the initial xml:
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${ldap.url}" />
<property name="base" value="${ldap.base}" />
<property name="userDn" value="${ldap.user}" />
<property name="password" value="${ldap.password}" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="ldapContextSource" />
</bean>
Can I move just this part to be a Java config ? Or would the references be an issue.
Thank You
You can move it to Java Config
Declare configuration class
#Configuration
public class AppConfig {
#Bean
public LdapContextSource ldapContextSource(){
LdapContextSource lcontext = new LdapContextSource();
lcontext.setUrl("${ldap.url}");
lcontext.setBase("${ldap.base}");
lcontext.setUserDn("${ldap.user}");
lcontext.setPassword("${ldap.password}");
return lcontext;
}
#Bean
public LdapTemplate LdapTemplate(){
LdapTemplate lTemplate = new LdapTemplate(ldapContextSource());
return lTemplate;
}
}
In your XML add
<context:annotation-config/>
<bean class="com.mypackage.AppConfig"/>

Is it possible to using expression on Spring SecurityMetadataSource?

i want to manage url authorization by Database. So, i'm implement Security MetadataSource. It was perfect except cann't using expression.
below is my code and xml settings.
xml
<beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:constructor-arg>
<beans:list>
<beans:bean class="org.springframework.security.access.vote.RoleVoter">
<beans:property name="rolePrefix" value="" />
</beans:bean>
</beans:list>
</beans:constructor-arg>
<beans:property name="allowIfAllAbstainDecisions" value="false" />
</beans:bean>
<beans:bean id="securityMetadataSource" class="my.package.CustomSecurityMetadataSource">
</beans:bean>
java
public class CustomSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
#Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
HttpServletRequest request = fi.getHttpRequest();
// TODO get url authorization from db and caching
String[] roles = new String[] { "ROLE_ANONYMOUS", "ROLE_USER"};
return SecurityConfig.createList(roles);
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
#Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
i want to using expression like hasAnyRole("ROLE_ADMIN", "ROLE_USER").
how can i use expression?

Spring Batch- Xml based configuration performance over Java based

I am trying to convert the spring batch configuration from xml based to annotation based.
Below is my xml based configuration.
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<!-- Step will need a transaction manager -->
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="dbMapper" class="org.test.DBValueMapper">
</bean>
<bean id="dbMapperFlatfile" class="org.test.FlatFileRowMapper">
</bean>
<bean id="paramSetter" class="org.test.DBParamSetter">
</bean>
<bean id="dbReader" class="org.test.DBValueReader"
scope="step">
<property name="paramSetter" ref="paramSetter"/>
<property name="verifyCursorPosition" value="false" />
<property name="dataSource" ref="dataSource" />
<property name="sql" value="#{jobParameters['SQL_QUERY']}" />
<property name="rowMapper" ref="dbMapper" />
<property name="fetchSize" value="5000" />
</bean>
<bean id="dbWriterIO" class="org.test.TemplateWritterIO"
scope="step">
<property name="velocityEngine" ref="velocityEngine" />
<!-- <property name="rptConfig" value="#{jobParameters['RPT_CONFIGVAL']}" /> -->
<property name="headerCallback" ref="dbWriterIO" />
<property name="footerCallback" ref="dbWriterIO" />
</bean>
<batch:job id="fileGenJobNio">
<batch:step id="fileGenJobStempNio">
<batch:tasklet>
<batch:chunk reader="dbReader" writer="dbWriterNIO"
commit-interval="5000">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Below is the equivalent Java based configuration:
#EnableBatchProcessing
#Import({ServiceConfiguration.class})
public class SRBatchGenerator extends DefaultBatchConfigurer{
#Autowired
private JobBuilderFactory jobBuilders;
#Autowired
private StepBuilderFactory stepBuilders;
#Autowired
private VelocityEngine velocityEngine;
#Autowired
private DBValueMapper mapper;
#Autowired
private DbHelper dbhelper;
#Autowired
private DataSource datasource;
#Bean
public Step step(){
return stepBuilders.get("step")
.chunk(5000)
.reader(reader())
//.processor(processor())
.writer(writer())
//.listener(logProcessListener())
.faultTolerant()
//.skipLimit(10)
//.skip(UnknownGenderException.class)
//.listener(logSkipListener())
.build();
}
#Bean
public Job fileGeneratorJob(){
return jobBuilders.get("fileGeneratorJob")
//.listener(protocolListener())
.start(step())
.build();
}
#Bean
public DBValueMapper mapper(){
return new DBValueMapper();
}
#Bean
#StepScope
public DBValueReader3 reader(){
String query="Select Test1,Test2,test3,test4 from RPt_TEST";
DBValueReader3 dbread = new DBValueReader3();
dbread.setSql(query);
dbread.setRowMapper(mapper);
dbread.setDataSource(datasource);
return dbread;
}
#Bean
#StepScope
public TemplateWritterIO writer(){
TemplateWritterIO writer=new TemplateWritterIO();
writer.setVelocityEngine(velocityEngine);
return writer;
}
#Override
protected JobRepository createJobRepository() throws Exception {
MapJobRepositoryFactoryBean factory =
new MapJobRepositoryFactoryBean();
factory.afterPropertiesSet();
return (JobRepository) factory.getObject();
}
}
When I execute my Job using xml based it took 27sec to write 1 Million record into Flat file.
But to write same 1 million record, Java based job took about 2 hours to write.
I am not sure what I am missing here. Can anyone help me or guide me why it is slow in Java based configuration.

Spring ContentNegotiatingViewResolver - How to use bean name for jsp view not full url with path parameters

My servlet-context file has
<beans:bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<beans:property name="useNotAcceptableStatusCode"
value="false" />
<beans:property name="contentNegotiationManager">
<beans:bean
class="org.springframework.web.accept.ContentNegotiationManager">
<beans:constructor-arg>
<beans:bean
class="org.springframework.web.accept.PathExtensionContentNegotiationStrategy">
<beans:constructor-arg>
<beans:map>
<beans:entry key="html" value="text/html" />
<beans:entry key="json" value="application/json" />
</beans:map>
</beans:constructor-arg>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
</beans:property>
<beans:property name="viewResolvers">
<beans:list>
<beans:bean
class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<beans:bean id="jspView"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/jsp/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
</beans:list>
</beans:property>
<beans:property name="defaultViews">
<beans:list>
<beans:bean
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
</beans:list>
</beans:property>
</beans:bean>
My Controller File has
#Controller("resources")
public class Resources {
#RequestMapping(value = "/resources/{name}", method = RequestMethod.GET)
public Map getResource(#PathVariable String name) {
return new HashMap();
}
}
But whenever i try to access /server/resources/myfilename.html
Server throws 404 saying /server/WEB-INF/jsp/resources/myfilename.jsp is not found.
But it should load /server/WEB-INF/jsp/resources.jsp as im using BeanNameViewResolver. Please help.
What you get:
Controller return a null view name, so DefaultRequestToViewNameTranslator generates one from URI = path from servlet path without slashes and filename extension: resources/myfilename
BeanNameViewResolver try to get a View from context named resources/myfilename, seem that fails and chain to next ViewResolver
InternalResourceViewResolver return a JstlView pointing to jsp prefix + viewName + suffix = /WEB-INF/jsp/resources/myfilename.jsp
So BeanNameViewResolver seems that don't resolve the view and the return of InternalResourceViewResolver is the expected one.
What you want (I think)
You want to remove the filename from the default view name, not only the extension.
Implements a RequestToViewNameTranslator and declare it in the DispatcherServlet context with name viewNameTranslator.
For example:
public class StripFileNameViewNameTranslator extends DefaultRequestToViewNameTranslator {
#Override
protected String transformPath(String lookupPath) {
String path = super.transformPath(lookupPath);
return StringUtils.substringBeforeLast(path, "/");
}
}

Resources