Using JavaConfig stepScope() in Groovy causes NullPointerException - groovy

After finding an accepted answer from a Spring Batch dev here, as well as the accompanying JavaConfig code below that, I am still left a bit confused as to how to use stepScope(). I am trying to scope the multiResourceItemReader below, but simply adding the bean definition of stepScope() to the top or bottom of the file causes this error:
Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'readFiles' defined in class path resource [com/onlinephotosubmission/csvImporter/service/BatchJobService.class]:
Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [java.lang.Object]: Factory method 'readFiles' threw exception;
nested exception is java.lang.NullPointerException:
Cannot invoke method get() on null object
All I know is that stepScope() has to be in a #Configuration file, other than that, I'm thoroughly confused as to what needs to be done.
BatchJobService.groovy
#Configuration
#EnableBatchProcessing
class BatchJobService {
#Autowired
JobBuilderFactory jobBuilderFactory
#Autowired
StepBuilderFactory stepBuilderFactory
#Autowired
JobLauncher jobLauncher
#Autowired
AfterJobListener afterJobListener
// Set values from properties file
#Value('${input.directory:file:inputs/*.csv}')
Resource[] resources
#Value('${report.directory:output}')
String reportsDir
#Value('${completed.directory:completed}')
String completedDir
#Value('${report.name.prepend:people}')
String prependName
#Value('${timestamp.format:dd_MM_yyyy_HH_mm_ss}')
String timestampFormat
// End set properties
#Bean
StepScope stepScope() {
final StepScope stepScope = new StepScope()
stepScope.setAutoProxy(true)
return stepScope
}
#Bean
Job readFiles() {
return jobBuilderFactory
.get("readFiles")
.incrementer(new RunIdIncrementer())
.flow(step1())
.end()
.listener(afterJobListener)
.build()
}
#Bean
Step step1() {
return stepBuilderFactory
.get("step1")
//NOTE: may need to adjust chunk size larger (say 1000 to take all transacions at once)
// or smaller (say 1 to take each transaction individually).
// Bigger is usually better, though.
.<Person, Person>chunk(1000)
.reader(multiResourceItemReader())
.processor(modifier())
.writer(writer())
.build()
}
#Bean
MultiResourceItemReader<Person> multiResourceItemReader() {
MultiResourceItemReader<Person> resourceItemReader = new MultiResourceItemReader<Person>()
resourceItemReader.setResources(resources)
resourceItemReader.setDelegate(reader())
return resourceItemReader
}
#Bean
FlatFileItemReader<Person> reader() {
FlatFileItemReader<Person> reader = new FlatFileItemReader<Person>()
reader.setLinesToSkip(1) //skips header line
reader.setLineMapper(new DefaultLineMapper()
{{
setLineTokenizer(new DelimitedLineTokenizer(",")
{{
setNames(["email", "identifier"] as String[])
}})
setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>() // BeanWrapperFieldSetMapper maps the line token values to a POJO directly by name
{{
setTargetType(Person.class)
}})
}})
return reader
}
#Bean
PersonItemProcessor modifier(){
return new PersonItemProcessor()
}
#Bean
FlatFileItemWriter<Person> writer() {
FlatFileItemWriter<Person> writer = new FlatFileItemWriter<>()
writer.setAppendAllowed(true)
writer.setResource(new FileSystemResource(reportsDir + "/" + prependName + getTime() + ".csv"))
writer.setLineAggregator(new DelimitedLineAggregator<Person>()
{{
setDelimiter(",")
setFieldExtractor(new BeanWrapperFieldExtractor<Person>()
{{
setNames(["status", "email", "identifier"] as String[])
}})
}})
return writer
}
}

The #EnableBatchProcessing automatically imports the StepScope, so you don't need to declare it as a bean in your application context. The issue you are linking to happens when there is a mix between XML and Java Config. In your case here, I see only Java Config so the issue should not happen.
I am trying to scope the multiResourceItemReader below
All I know is that stepScope() has to be in a #Configuration file, other than that, I'm thoroughly confused as to what needs to be done.
Just declaring the step scope is not enough, you need to add the #StepScope annotation on the bean definition.
You can find more details about the StepScope in the reference documentation here: https://docs.spring.io/spring-batch/4.0.x/reference/html/step.html#step-scope

Related

How to mock the custom util class

How can I mock the custom util class? I am getting the error below:
[ERROR] 2019-08-20 12:06:02:197 [] com.metlife.api.clientlibrary.util.JWSRequestUtil.prepareRequestJWS():71 - Exception in preparing JWS request ::
java.lang.NullPointerException: null
The code is:
public class EPartnerPromotionHelperTest {
#InjectMocks
EPartnerPromotionHelper ePartnerPromotionHelper;
#Mock
private JWSRequestUtil jwsRequestUtil;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testSavePromotion() throws Exception {
String loggerId = "loggerId";
PromotionDTO promotionDTO = new PromotionDTO();
promotionDTO.setDescription("description");
promotionDTO.setCreationDate(new Date());
promotionDTO.setModifiedDate(new Date());
Mockito.when(jwsRequestUtil.prepareRequestJWS(Matchers.any(EPartnerRestRequestDTO.class)
,Matchers.any(Boolean.class))).thenReturn("test");
PromotionDTO response =ePartnerPromotionHelper.savePromotion(loggerId,promotionDTO);
assertNotNull(response);
}
}
Assuming that the error message comes from the invocation of your prepareRequestJWS method, you can change your syntax and use doReturn instead.
Mockito.doReturn("test")
.when(jwsRequestUtil)
.prepareRequestJWS(Matchers.any(EPartnerRestRequestDTO.class),
Matchers.any(Boolean.class));
Writing it like this the prepareRequestJWS method won't be invoked, check the Overriding a previous exception-stubbing part that is described in the javadoc of the doReturn method. This also applies to normal methods that would throw Exception's when they are invoked.
However a question would be why this exception is coming from your JwsRequestUtil class in the first place. Feel free to add the relevant code to your question.

Spring Integration JdbcMessageStore casting error

I am trying to create service that will read some data from remote server and process them using Spring Integration.
I have class that extends ArrayList, because I need to keep pointer to other page, so I can read it in next remote call. I set up release strategy to collect all these pages, until there is no pointer for the next page.
Here is definition of class:
public class CustomList extends ArrayList<DataInfo>
{
private String nextCursor;
// Methods omitted for readability
}
Everything worked fine, until I setup JdbcMessageStore in Aggregator, so I can keep messages in case of service shutdown.
Problem on which I come across is that in my release strategy class I cast my list class to same class (because message group does not define type), this exceptions is raised:
java.lang.ClassCastException: com.example.CustomList cannot be cast to com.example.CustomList
This is my release strategy class:
#Component
public class CursorReleaseStrategy implements ReleaseStrategy
{
#Override
public boolean canRelease(MessageGroup group)
{
return group.getMessages().stream()
.anyMatch(message -> ((CustomList) message.getPayload()).getNextCursor() == null);
}
}
If I remove message store, everything works fine, but the problem is that I need message store.
I am using spring boot 2.1.6 and Spring Integration DSL for creating this flow.
From what I read, this error happens because of different class loaders, but this I do from the same application.
Is there anything more that I need to configure for this to work_
Almost certainly a class loader issue; you can find which class loader loads each component (message store, release strategy) by injecting them into a bean and calling getClass().getClassLoader().
When application has been packaged in jar, there was such error.
So to fix the problem I created two beans, depending on profile.
For example:
#Profile("!prod")
#Bean
public MessageGroupStore messageStore(DataSource dataSource)
{
JdbcMessageStore jdbcMessageStore = new JdbcMessageStore(dataSource);
jdbcMessageStore.setDeserializer(inputStream -> {
ConfigurableObjectInputStream objectInputStream = new ConfigurableObjectInputStream(inputStream, Thread.currentThread().getContextClassLoader());
try {
return (Message<?>) objectInputStream.readObject();
} catch (ClassNotFoundException var4) {
throw new NestedIOException("Failed to deserialize object type", var4);
}
});
return jdbcMessageStore;
}
#Profile("prod")
#Bean
public MessageGroupStore prodMessageStore(DataSource dataSource)
{
return new JdbcMessageStore(dataSource);
}

JSF with custom Annotation

I am using primefaces version 5 and I am adding messages regarding to a specific actions like save :
public void save(Tenant tenant) {
tenantDao.save(tenant);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Save success"));
}
Since I had a lot of these actions I tried to simplify this by creating a custom annotation called Message:
#Target(Runtime)
#Retention(Method)
public #interface Message {
String value();
}
and in my dao class :
public class TenantDao {
#Message("Saved Successfully")
public Tenant save(Tenant t) {
return em.save(t);
}
}
To read this annotation I have overridden the ELResolver the method invoke
public Object invoke(ELContext context,
Object base,
Object method,
Class<?>[] paramTypes,
Object[] params) {
Object result = super.invoke(context,method,paramTypes,params);
Method m = base.getClass().getMethod(method,paramTypes);
if(m.getAnnotation(Message.class) != null) {
addMessahe(m.getAnnotation(Message.class).value());
}
return result;
}
This was called in property (rendered, update, ..) but not in action listener
After a lot of debugging I discovered that theactionListener is called from MethodExpression class. So, I wrapped the MethodExpression class, and override the method invoke.
The problem now is that there are no way to retreive the class Method from MethodExpression class, also if I used the expression #{tenantDao.save(tenant)} the method getMethodInfo from MethodExpression will throw an exception.
Are there any way to read tAnnotation from any jsf context ?
I know that using Spring with AOP may solve this but I am not using Spring now.
Thanks

"Null correlation not allowed" when trying to use bean with #CorrelationStrategy annotation

I am new to Spring Integration and am trying to use Java DSL configuration to specify a flow that aggregates messages using an class GroupPublishAggregator that has #Aggregator, #ReleaseStrategy, and #CorrelationStrategy annotations.
I suspect that I'm making a newbie mistake in the configuration, because what I see when the aggregator receives a message is "java.lang.IllegalStateException: Null correlation not allowed. Maybe the CorrelationStrategy is failing?" If I debug the framework code, I see that the AbstractCorrelatingMessageHandler is invoking the default org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy instead of my strategy.
The configuration code looks like:
#Bean
public GroupPublishAggregator publishAggregator() {
// This class has methods with #Aggregator, #ReleaseStrategy,
// and #CorrelationStrategy annotations.
return new GroupPublishAggregator();
}
#Bean
public IntegrationFlow publish() {
return IntegrationFlows.from(this.inputChannel())
.wireTap("monitor")
.aggregate(new Consumer<AggregatorSpec>() {
#Override
public void accept(AggregatorSpec aggregatorSpec) {
aggregatorSpec.processor(publishAggregator(), null);
}
})
.get();
}
This is a bug. Will be fixed soon: https://github.com/spring-projects/spring-integration-java-dsl/issues/93
Meanwhile the workaround for you is like to use CorrelationStrategyFactoryBean and ReleaseStrategyFactoryBean as #Bean and with particular correlationStrategy(CorrelationStrategy correlationStrategy) and releaseStrategy(ReleaseStrategy releaseStrategy), respectively.
The problem that we don't call afterPropertiesSet() for those FactoryBeans.

i want to launch a class method periodically using spring

i have the following code.
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(10000);
executor.setThreadNamePrefix("Executor-");
executor.initialize();
return executor;
}
}
and if i want to run the recommend method after every certain interval of time. What can be the java spring bean configuration way to do that.?
public class UserBrandsRecommender {
public List<RecommendedItem> recommend(Long userId, int number) throws TasteException{
}
}
You should look into the #Scheduled annotation. For example:
#Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
You'll probably need to create a new Spring bean with a method similar to above. The bean could have the UserBrandsRecommender injected into it. The new bean will need to implement some logic to pass proper values for the "userId" and "number" parameters to the "recommend" method.
More information here:
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/htmlsingle/#scheduling-annotation-support

Resources