title: Spring Batch job throwing StackOverflowError: null error when started
body: <p>I have a simple controller that takes a JSON string of file paths and runs a spring batch job on these files. To implement spring batch I am following a tutorial that would eventually produce the code in <a href="https://github.com/michaelhoffmantech/patient-batch-loader" rel="nofollow noreferrer">https://github.com/michaelhoffmantech/patient-batch-loader</a>.</p>
  
  <pre><code>@RestController
  public class JobController {
      private static final Log _logger = LogFactory.getLog(JobController.class);
      @Autowired
      private JobLauncher jobLauncher;
      @Autowired
      private Job job;
  
      @RequestMapping(value = "/job/import", method = RequestMethod.POST)
      public void importAsset(@RequestBody String uploadedFiles) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
          _logger.debug("importAsset() &gt;&gt;");
          try{
              _logger.debug("Uploaded files: ");
              _logger.debug(uploadedFiles);
  
              _logger.debug("Putting values into the parameter map...");
              Map&lt;String, JobParameter&gt; parameterMap = new LinkedHashMap&lt;&gt;();
              parameterMap.put(Constants.JOB_PARAM_UPLOADED_FILES, new JobParameter(uploadedFiles));
  
              _logger.debug("Launching job [" + job.getName() + "]...");
              jobLauncher.run(job, new JobParameters(parameterMap));
              _logger.debug("&lt;&lt; importAsset()");
          }
          catch (Exception e){
              String errorMessage = "An error occured while importing a batch. " + e.getLocalizedMessage();
              _logger.error(errorMessage, e);
              throw e;
          }
      }
  }
  </code></pre>
  
  <p>Batch configuration is:</p>
  
  <pre><code>@Configuration
  @EnableBatchProcessing
  public class BatchConfig implements BatchConfigurer {
      @Autowired
      private PlatformTransactionManager platformTransactionManager;
  
      @Autowired
      private DataSource dataSource;
  
      private JobRepository jobRepository;
      private JobExplorer jobExplorer;
      private JobLauncher jobLauncher;
  
  
      @Override
      public JobRepository getJobRepository() throws Exception {
          return this.jobRepository;
      }
  
      @Override
      public PlatformTransactionManager getTransactionManager() throws Exception {
          return this.platformTransactionManager;
      }
  
      @Override
      public JobLauncher getJobLauncher() throws Exception {
          return this.jobLauncher;
      }
  
      @Override
      public JobExplorer getJobExplorer() throws Exception {
          return this.jobExplorer;
      }
  
      protected JobLauncher createJobLauncher() throws Exception {
          SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
          jobLauncher.setJobRepository(this.jobRepository);
          jobLauncher.afterPropertiesSet();
          return jobLauncher;
      }
  
      protected JobRepository createJobRepository() throws Exception {
          JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
          factory.setDataSource(this.dataSource);
          factory.setTransactionManager(getTransactionManager());
          factory.afterPropertiesSet();
          return factory.getObject();
      }
  
      @PostConstruct
      public void afterPropertiesSet() throws Exception{
          this.jobRepository = createJobRepository();
          JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
          jobExplorerFactoryBean.setDataSource(this.dataSource);
          jobExplorerFactoryBean.afterPropertiesSet();
          this.jobExplorer = jobExplorerFactoryBean.getObject();
          this.jobLauncher = createJobLauncher();
      }
  }
  </code></pre>
  
  <p>And finally the job configuration is:</p>
  
  <pre><code>@Configuration
  public class BatchJobConfig {
      private static final Log _logger = LogFactory.getLog(BatchJobConfig.class);
  
      @Autowired
      private JobBuilderFactory jobBuilderFactory;
      @Autowired
      private StepBuilderFactory stepBuilderFactory;
  
      @Bean
      JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry){
          JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
          postProcessor.setJobRegistry(jobRegistry);
          return postProcessor;
      }
  
      @Bean
      public Step step() throws Exception{
          return this.stepBuilderFactory
                  .get(Constants.STEP_NAME)
                  .tasklet(new Tasklet() {
                      @Override
                      public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                          _logger.debug("execute() &gt;&gt;");
                          _logger.debug("&lt;&lt; execute()");
                          return RepeatStatus.FINISHED;
                      }
                  })
                  .build();
      }
  
      @Bean
      public Job job(Step step){
          return this.jobBuilderFactory
                  .get(Constants.JOB_NAME)
                  .validator(validator())
                  .start(step)
                  .build();
      }
  
      public JobParametersValidator validator(){
          return new JobParametersValidator(){
              @Override
              public void validate(JobParameters parameters) throws JobParametersInvalidException {
                  _logger.debug("validate() &gt;&gt;");
  
                  String filePathsJsonStr = parameters.getString(Constants.JOB_PARAM_UPLOADED_FILES);
                  if(StringUtils.isBlank(filePathsJsonStr)){
                      throw new JobParametersInvalidException("'" + Constants.JOB_PARAM_UPLOADED_FILES  + "' parameter is required for job '" + Constants.JOB_NAME + "'.");
                  }
  
                  Gson gson = new Gson();
                  Type listType = new TypeToken&lt;ArrayList&lt;UploadFile&gt;&gt;(){}.getType();
                  ArrayList&lt;UploadFile&gt; uploadedFiles = gson.fromJson(filePathsJsonStr, listType);
  
                  for(UploadFile uploadFile: uploadedFiles){
                      File file = new File(uploadFile.getPath());
                      if(!file.exists()){
                          throw new JobParametersInvalidException("File '" + uploadFile.getPath() + "' did not exist or was not readable.");
                      }
                  }
  
                  _logger.debug("&lt;&lt; validate()");
              }
          };
      }
  }
  </code></pre>
  
  <p>When I run the batch job through the REST method. I get the following in the log:</p>
  
  <pre><code>2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : importAsset() &gt;&gt;
  2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : Uploaded files: 
  2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : [{"path":"C:\\app-import-staging\\cat-pet-animal-domestic-104827.jpeg","username":"test"},{"path":"C:\\app-import-staging\\kittens-cat-cat-puppy-rush-45170.jpeg","username":"test"}]
  2018-09-29 20:00:51.680 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : Putting values into the parameter map...
  2018-09-29 20:00:51.681 DEBUG 10104 --- [nio-8102-exec-2] c.g.m.t.t.controllers.JobController      : Launching job [app-import]...
  2018-09-29 20:00:51.745 ERROR 10104 --- [nio-8102-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause
  
  java.lang.StackOverflowError: null
      at java.util.HashMap.putVal(HashMap.java:657) ~[na:1.8.0_181]
      at java.util.HashMap.put(HashMap.java:612) ~[na:1.8.0_181]
      at java.util.HashSet.add(HashSet.java:220) ~[na:1.8.0_181]
      at java.util.Collections$SynchronizedCollection.add(Collections.java:2035) ~[na:1.8.0_181]
      at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:508) ~[na:1.8.0_181]
      at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
      at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
      at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
      at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
  </code></pre>
  
  <p>What I noticed was that the part:</p>
  
  <pre><code>at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at com.sun.proxy.$Proxy83.getTransaction(Unknown Source) ~[na:na]
      at sun.reflect.GeneratedMethodAccessor93.invoke(Unknown Source) ~[na:na]
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
      at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
      at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.14.RELEASE.jar:4.3.14.RELEASE]
      at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-3.0.8.RELEASE.jar:3.0.8.RELEASE]
  </code></pre>
  
  <p>goes on and on until it throws a StackOverflowError.</p>
  
  <p>Any advice or help on what to change to fix this would be appreciated.</p>