Is there a way to access bean which is defined as @JobScope
in partitioned step?
We defined http client bean as @JobScope
since it is unique per job but dynamically created and we need it in slave steps to issue post requests. When we autowire everything we get
Error creating bean with name 'scopedTarget.captureErpStockTasklet':
Scope 'step' is not active for the current thread; consider defining a
scoped proxy for this bean if you intend to refer to it from a singleton;
nested exception is java.lang.IllegalStateException: No context holder
available for step scope
Here is job configuration class (I have removed everything not needed, left only configuration of partitioned step and Client
class which is in @JobScope
because on every job run it needs to be job specific:
@Configuration
@EnableBatchProcessing
@ComponentScan(basePackages = {"com.example.importjob"})
public class FrozenFileImportJobConfig {
@Value("${thread.pool:10}")
private int threadPoolSize;
@Value("${partitioner.max.thread.pool:10}")
private int maxThreadPoolSize;
@Value("${grid.size:10}")
private int gridSize;
@Value("${client.url:some_url}")
private int url;
@Value("${client.id:client_id}")
private int clientId;
@Value("${client.secret:secret}")
private int clientSecret;
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private ErpStockIdPartitioner erpStockIdPartitioner;
@Autowired
private CaptureErpStockTasklet captureErpStockTasklet;
@Bean(destroyMethod = "destroy")
@JobScope
public Client client() {
return new Client(url, clientId, clientSecret);
}
public ExecutionContextPromotionListener contextPromotionListener() {
final ExecutionContextPromotionListener executionContextPromotionListener = new ExecutionContextPromotionListener();
executionContextPromotionListener.setKeys(new String[] {"erp_stock_ids"});
return executionContextPromotionListener;
}
public Step captureErpStockDBTaskletStep() {
return stepBuilderFactory.get("captureErpStockDBTaskletStep").tasklet(captureErpStockTasklet).build();
}
@Bean
public ThreadPoolTaskExecutor erpStockIdTaskExecutor() {
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(threadPoolSize);
threadPoolTaskExecutor.setMaxPoolSize(maxThreadPoolSize);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
return threadPoolTaskExecutor;
}
public Step partitioningCaptureStep() {
return stepBuilderFactory.get("partitioningCaptureStep").partitioner(captureErpStockDBTaskletStep())
.partitioner("erpStockIdPartitioner", erpStockIdPartitioner).taskExecutor(erpStockIdTaskExecutor())
.gridSize(gridSize).allowStartIfComplete(true).build();
}
@Bean
public Job riverIslandFrozenFileImportJob() {
return jobBuilderFactory.get("riverIslandFrozenFileImportJob").start(partitioningCaptureStep()).build();
}
}
And here is captureErpStockDBTasklet
which is invoked as partitioned step from master partitioningCaptureStep
:
@Component
@StepScope
public class CaptureErpStockTasklet implements Tasklet {
private static final Logger LOG = LoggerFactory.getLogger(CaptureErpStockTasklet.class);
@Value("#{jobParameters[" + FtpJobParameters.ORGANIZATION_ID + "]}")
private Long organizationId;
@Autowired
private ErpStockRepository erpStockRepository;
@Autowired
private Client client;
@Override
public RepeatStatus execute(final StepContribution contribution, final ChunkContext chunkContext) throws Exception {
//importing of erp stock from DB, nothing special in that code so removed
client.captureErpStock(stock);
LOG.info("[{}] Finished importing ERP {}", organizationId, erpStock);
return RepeatStatus.FINISHED;
}
}
When I change in configuration Clinet
to be @StepScope
it works fine but than I get client created for every step which is not what I want, I want to have one for entire job.