private SimpleJob importProductsJob() throws Exception {
    // Create Job
    SimpleJob bean = new SimpleJob();
    bean.setName("importProductsJob");
    bean.setJobRepository(jobRepository);
    bean.afterPropertiesSet();

    // Create Steps
    List<Step> steps = new ArrayList<Step>();

    // Create Tasklet Step
    TaskletStep step = new TaskletStep();
    step.setName("importProducts");
    step.setTransactionManager(transactionManager);
    step.setJobRepository(jobRepository);
    step.setStartLimit(100);

    // Create repeat template for Chunk Size
    RepeatTemplate repeatTemplate = new TaskExecutorRepeatTemplate();
    repeatTemplate.setCompletionPolicy(new SimpleCompletionPolicy(5));
    // TaskletStep <- RepeatTemplate
    step.setStepOperations(repeatTemplate);

    // Create Chunk Tasklet with Provider (reader) and Chunk Processor
    // Tasklet <- ChunkProvider <- ItemReader, RepeatTemplate
    // Tasklet <- ChunkProcessor <- ItemProcessor, ItemWriter
    ChunkOrientedTasklet<Product> tasklet =
        new ChunkOrientedTasklet<Product>(
            new SimpleChunkProvider<Product>(productReader(), repeatTemplate),
            new SimpleChunkProcessor<Product, Product>(
                new PassThroughItemProcessor<Product>(), new ProductItemLoggerWriter()));

    // Job <- Steps <- TaskletStep <- Tasklet
    step.setTasklet(tasklet);
    // Job <- Steps <- TaskletStep
    steps.add(step);
    // Job <- Steps
    bean.setSteps(steps);
    return bean;
  }
  /** @return fully configured retry template for item processing phase. */
  private BatchRetryTemplate createRetryOperations() {

    RetryPolicy retryPolicy = this.retryPolicy;
    SimpleRetryPolicy simpleRetryPolicy = null;

    Map<Class<? extends Throwable>, Boolean> map =
        new HashMap<Class<? extends Throwable>, Boolean>(retryableExceptionClasses);
    map.put(ForceRollbackForWriteSkipException.class, true);
    simpleRetryPolicy = new SimpleRetryPolicy(retryLimit, map);

    if (retryPolicy == null) {
      Assert.state(
          !(retryableExceptionClasses.isEmpty() && retryLimit > 0),
          "If a retry limit is provided then retryable exceptions must also be specified");
      retryPolicy = simpleRetryPolicy;
    } else if ((!retryableExceptionClasses.isEmpty() && retryLimit > 0)) {
      CompositeRetryPolicy compositeRetryPolicy = new CompositeRetryPolicy();
      compositeRetryPolicy.setPolicies(new RetryPolicy[] {retryPolicy, simpleRetryPolicy});
      retryPolicy = compositeRetryPolicy;
    }

    RetryPolicy retryPolicyWrapper = getFatalExceptionAwareProxy(retryPolicy);

    BatchRetryTemplate batchRetryTemplate = new BatchRetryTemplate();
    if (backOffPolicy != null) {
      batchRetryTemplate.setBackOffPolicy(backOffPolicy);
    }
    batchRetryTemplate.setRetryPolicy(retryPolicyWrapper);

    // Co-ordinate the retry policy with the exception handler:
    RepeatOperations stepOperations = getStepOperations();
    if (stepOperations instanceof RepeatTemplate) {
      SimpleRetryExceptionHandler exceptionHandler =
          new SimpleRetryExceptionHandler(
              retryPolicyWrapper, getExceptionHandler(), nonRetryableExceptionClasses);
      ((RepeatTemplate) stepOperations).setExceptionHandler(exceptionHandler);
    }

    if (retryContextCache != null) {
      batchRetryTemplate.setRetryContextCache(retryContextCache);
    }

    if (retryListeners != null) {
      batchRetryTemplate.setListeners(retryListeners.toArray(new RetryListener[0]));
    }
    return batchRetryTemplate;
  }
  /**
   * Build the step from the components collected by the fluent setters. Delegates first to {@link
   * #enhance(Step)} and then to {@link #createTasklet()} in subclasses to create the actual
   * tasklet.
   *
   * @return a tasklet step fully configured and read to execute
   */
  public TaskletStep build() {

    registerStepListenerAsChunkListener();

    TaskletStep step = new TaskletStep(getName());

    super.enhance(step);

    step.setChunkListeners(chunkListeners.toArray(new ChunkListener[0]));

    if (transactionAttribute != null) {
      step.setTransactionAttribute(transactionAttribute);
    }

    if (stepOperations == null) {

      stepOperations = new RepeatTemplate();

      if (taskExecutor != null) {
        TaskExecutorRepeatTemplate repeatTemplate = new TaskExecutorRepeatTemplate();
        repeatTemplate.setTaskExecutor(taskExecutor);
        repeatTemplate.setThrottleLimit(throttleLimit);
        stepOperations = repeatTemplate;
      }

      ((RepeatTemplate) stepOperations).setExceptionHandler(exceptionHandler);
    }
    step.setStepOperations(stepOperations);
    step.setTasklet(createTasklet());

    step.setStreams(streams.toArray(new ItemStream[0]));

    try {
      step.afterPropertiesSet();
    } catch (Exception e) {
      throw new StepBuilderException(e);
    }

    return step;
  }
 private RepeatOperations createRepeatOperations() {
   RepeatTemplate repeatOperations = new RepeatTemplate();
   repeatOperations.setCompletionPolicy(getChunkCompletionPolicy());
   repeatOperations.setExceptionHandler(getExceptionHandler());
   return repeatOperations;
 }