Search code examples
spring-batchspring-batch-admin

Spring Batch Admin throws ConcurrentModificationException after every execution


As I get deeper into it, I find I love the spring batch admin application.

However, after every job execution I see this exception being thrown.

Environment

OS: Windows 7
Java: jdk 1.8.0_25
Spring Batch Admin Sample version: 1.3.1
Spring version: 3.2.13   * stock 3.2.9 has a bug that causes other symptoms 
Spring-batch version: 3.0.2
Pivotal tc version:  3.0 Developer Edition
IDE: STS 3.6.3

Log Snip:

14:33:36.246 [pool-1-thread-1] ERROR o.s.s.s.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task.
java.util.ConcurrentModificationException: null
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) ~[na:1.8.0_25]
    at java.util.ArrayList$Itr.remove(ArrayList.java:865) ~[na:1.8.0_25]
    at org.springframework.batch.admin.service.SimpleJobService.removeInactiveExecutions(SimpleJobService.java:498) ~[spring-batch-admin-manager-1.3.1.MAXIS-MOD.jar:na]
    at sun.reflect.GeneratedMethodAccessor196.invoke(Unknown Source) ~[na:na]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64) ~[spring-context-3.2.13.RELEASE.jar:3.2.13.RELEASE]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53) ~[spring-context-3.2.13.RELEASE.jar:3.2.13.RELEASE]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_25]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [na:1.8.0_25]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_25]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [na:1.8.0_25]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_25]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_25]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25]

My code doesn't touch the core of spring batch admin, so I'm a bit bewildered.

This is not a showstopper (yet), so I am open to suggestions (including eventually contributing a patch).


Solution

  • The reason for the exception is a buggy implementation of org.springframework.batch.admin.service.SimpleJobService, at least in 1.3.1.RELEASE:

    private Collection<JobExecution> activeExecutions = Collections.synchronizedList(new ArrayList<JobExecution>());
    
    
    public void removeInactiveExecutions() {
                for (Iterator<JobExecution> iterator = activeExecutions.iterator(); iterator.hasNext();) {
                    JobExecution jobExecution = iterator.next();
        ...
                    if (!jobExecution.isRunning()) {
                        iterator.remove();
                    }
                }
    

    Calling iterator.getNext(); AFTER a previous call to iterator.remove(); on a java.util.ArrayList - even synchronized - is not a good idea...

    I am currently overriding the Spring Batch Admin configuration, using my own implementation of JobService for bean with id="jobService":

    META-INF\spring\batch\override\execution-context.xml
    
    ...
    <bean id="jobService" class="com.foo.springbatch.MyPatchedSimpleJobServiceFactoryBean">
        <property name="jobRepository" ref="jobRepository" />
        <property name="jobLauncher" ref="jobLauncher" />
        <property name="jobLocator" ref="jobRegistry" />
        <property name="dataSource" ref="dataSource" />
    </bean>
    ...