diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java index 6cae63441..50c556a89 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/context/StepSynchronizationManager.java @@ -126,6 +126,7 @@ public class StepSynchronizationManager { if (remaining <= 0) { synchronized (contexts) { contexts.remove(current); + counts.remove(current); } } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java index fb55942dd..e06bf5fae 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/scope/context/StepSynchronizationManagerTests.java @@ -4,8 +4,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -17,10 +19,12 @@ import org.junit.Before; import org.junit.Test; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepExecution; +import org.springframework.util.ReflectionUtils; public class StepSynchronizationManagerTests { - private StepExecution stepExecution = new StepExecution("step", new JobExecution(0L)); + private StepExecution stepExecution = new StepExecution("step", + new JobExecution(0L)); @Before @After @@ -38,9 +42,10 @@ public class StepSynchronizationManagerTests { } @Test - public void testClose() { + public void testClose() throws Exception { final List list = new ArrayList(); - StepContext context = StepSynchronizationManager.register(stepExecution); + StepContext context = StepSynchronizationManager + .register(stepExecution); context.registerDestructionCallback("foo", new Runnable() { public void run() { list.add("foo"); @@ -49,35 +54,51 @@ public class StepSynchronizationManagerTests { StepSynchronizationManager.close(); assertNull(StepSynchronizationManager.getContext()); assertEquals(0, list.size()); + // check for possible memory leak + assertEquals(0, extractStaticMap("counts").size()); + assertEquals(0, extractStaticMap("contexts").size()); + } + + @SuppressWarnings("unchecked") + private Map extractStaticMap(String name) throws IllegalAccessException { + Field field = ReflectionUtils.findField( + StepSynchronizationManager.class, name); + ReflectionUtils.makeAccessible(field); + Map map = (Map) field.get(StepSynchronizationManager.class); + return map; } @Test public void testMultithreaded() throws Exception { - StepContext context = StepSynchronizationManager.register(stepExecution); + StepContext context = StepSynchronizationManager + .register(stepExecution); ExecutorService executorService = Executors.newFixedThreadPool(2); - FutureTask task = new FutureTask(new Callable() { - public StepContext call() throws Exception { - try { - StepSynchronizationManager.register(stepExecution); - StepContext context = StepSynchronizationManager.getContext(); - context.setAttribute("foo", "bar"); - return context; - } - finally { - StepSynchronizationManager.close(); - } - } - }); + FutureTask task = new FutureTask( + new Callable() { + public StepContext call() throws Exception { + try { + StepSynchronizationManager.register(stepExecution); + StepContext context = StepSynchronizationManager + .getContext(); + context.setAttribute("foo", "bar"); + return context; + } finally { + StepSynchronizationManager.close(); + } + } + }); executorService.execute(task); executorService.awaitTermination(1, TimeUnit.SECONDS); - assertEquals(context.attributeNames().length, task.get().attributeNames().length); + assertEquals(context.attributeNames().length, task.get() + .attributeNames().length); StepSynchronizationManager.close(); assertNull(StepSynchronizationManager.getContext()); } @Test public void testRelease() { - StepContext context = StepSynchronizationManager.register(stepExecution); + StepContext context = StepSynchronizationManager + .register(stepExecution); final List list = new ArrayList(); context.registerDestructionCallback("foo", new Runnable() { public void run() {