BATCH-1701: Introduce job scope

This commit is contained in:
jpraet
2012-09-14 22:21:54 +02:00
committed by Michael Minella
parent e5fc0665cc
commit 2436d84210
43 changed files with 3145 additions and 339 deletions

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2006-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.configuration.xml;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import org.junit.Test;
import org.springframework.batch.core.scope.JobScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Thomas Risberg
* @author Jimmy Praet
*/
public class AutoRegisteringJobScopeTests {
@Test
public void testJobElement() throws Exception {
ConfigurableApplicationContext ctx =
new ClassPathXmlApplicationContext(
"org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForJobElementTests-context.xml");
@SuppressWarnings("unchecked")
Map<String, JobScope> beans = ctx.getBeansOfType(JobScope.class);
assertTrue("JobScope not defined properly", beans.size() == 1);
}
@Test
public void testStepElement() throws Exception {
ConfigurableApplicationContext ctx =
new ClassPathXmlApplicationContext(
"org/springframework/batch/core/configuration/xml/AutoRegisteringJobScopeForStepElementTests-context.xml");
@SuppressWarnings("unchecked")
Map<String, JobScope> beans = ctx.getBeansOfType(JobScope.class);
assertTrue("JobScope not defined properly", beans.size() == 1);
}
}

View File

@@ -0,0 +1,150 @@
package org.springframework.batch.core.scope;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.scope.context.JobContext;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class AsyncJobScopeIntegrationTests implements BeanFactoryAware {
private Log logger = LogFactory.getLog(getClass());
@Autowired
@Qualifier("simple")
private Collaborator simple;
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
private ListableBeanFactory beanFactory;
private int beanCount;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Before
public void countBeans() {
JobSynchronizationManager.release();
beanCount = beanFactory.getBeanDefinitionCount();
}
@After
public void cleanUp() {
JobSynchronizationManager.close();
// Check that all temporary bean definitions are cleaned up
assertEquals(beanCount, beanFactory.getBeanDefinitionCount());
}
@Test
public void testSimpleProperty() throws Exception {
JobExecution jobExecution = new JobExecution(11L);
ExecutionContext executionContext = jobExecution.getExecutionContext();
executionContext.put("foo", "bar");
JobSynchronizationManager.register(jobExecution);
assertEquals("bar", simple.getName());
}
@Test
public void testGetMultipleInMultipleThreads() throws Exception {
List<FutureTask<String>> tasks = new ArrayList<FutureTask<String>>();
for (int i = 0; i < 12; i++) {
final String value = "foo" + i;
final Long id = 123L + i;
FutureTask<String> task = new FutureTask<String>(new Callable<String>() {
public String call() throws Exception {
JobExecution jobExecution = new JobExecution(id);
ExecutionContext executionContext = jobExecution.getExecutionContext();
executionContext.put("foo", value);
JobContext context = JobSynchronizationManager.register(jobExecution);
logger.debug("Registered: " + context.getJobExecutionContext());
try {
return simple.getName();
}
finally {
JobSynchronizationManager.close();
}
}
});
tasks.add(task);
taskExecutor.execute(task);
}
int i = 0;
for (FutureTask<String> task : tasks) {
assertEquals("foo" + i, task.get());
i++;
}
}
@Test
public void testGetSameInMultipleThreads() throws Exception {
List<FutureTask<String>> tasks = new ArrayList<FutureTask<String>>();
final JobExecution jobExecution = new JobExecution(11L);
ExecutionContext executionContext = jobExecution.getExecutionContext();
executionContext.put("foo", "foo");
JobSynchronizationManager.register(jobExecution);
assertEquals("foo", simple.getName());
for (int i = 0; i < 12; i++) {
final String value = "foo" + i;
FutureTask<String> task = new FutureTask<String>(new Callable<String>() {
public String call() throws Exception {
ExecutionContext executionContext = jobExecution.getExecutionContext();
executionContext.put("foo", value);
JobContext context = JobSynchronizationManager.register(jobExecution);
logger.debug("Registered: " + context.getJobExecutionContext());
try {
return simple.getName();
}
finally {
JobSynchronizationManager.close();
}
}
});
tasks.add(task);
taskExecutor.execute(task);
}
int i = 0;
for (FutureTask<String> task : tasks) {
assertEquals("foo", task.get());
i++;
}
// Don't close the outer scope until all tasks are finished. This should
// always be the case if using an AbstractJob
JobSynchronizationManager.close();
}
}

View File

@@ -0,0 +1,87 @@
package org.springframework.batch.core.scope;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobScopeDestructionCallbackIntegrationTests {
@Autowired
@Qualifier("proxied")
private Job proxied;
@Autowired
@Qualifier("nested")
private Job nested;
@Autowired
@Qualifier("ref")
private Job ref;
@Autowired
@Qualifier("foo")
private Collaborator foo;
@Before
@After
public void resetMessage() throws Exception {
TestDisposableCollaborator.message = "none";
TestAdvice.names.clear();
}
@Test
public void testDisposableScopedProxy() throws Exception {
assertNotNull(proxied);
proxied.execute(new JobExecution(1L));
assertEquals(1, StringUtils.countOccurrencesOf(TestDisposableCollaborator.message, "destroyed"));
}
@Test
public void testDisposableInnerScopedProxy() throws Exception {
assertNotNull(nested);
nested.execute(new JobExecution(1L));
assertEquals(1, StringUtils.countOccurrencesOf(TestDisposableCollaborator.message, "destroyed"));
}
@Test
public void testProxiedScopedProxy() throws Exception {
assertNotNull(nested);
nested.execute(new JobExecution(1L));
assertEquals(4, TestAdvice.names.size());
assertEquals("bar", TestAdvice.names.get(0));
assertEquals(1, StringUtils.countOccurrencesOf(TestDisposableCollaborator.message, "destroyed"));
}
@Test
public void testRefScopedProxy() throws Exception {
assertNotNull(ref);
ref.execute(new JobExecution(1L));
assertEquals(4, TestAdvice.names.size());
assertEquals("spam", TestAdvice.names.get(0));
assertEquals(2, StringUtils.countOccurrencesOf(TestDisposableCollaborator.message, "destroyed"));
assertEquals(1, StringUtils.countOccurrencesOf(TestDisposableCollaborator.message, "bar:destroyed"));
assertEquals(1, StringUtils.countOccurrencesOf(TestDisposableCollaborator.message, "spam:destroyed"));
}
@Test
public void testProxiedNormalBean() throws Exception {
assertNotNull(nested);
String name = foo.getName();
assertEquals(1, TestAdvice.names.size());
assertEquals(name, TestAdvice.names.get(0));
}
}

View File

@@ -0,0 +1,115 @@
package org.springframework.batch.core.scope;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobScopeIntegrationTests {
@Autowired
@Qualifier("vanilla")
private Job vanilla;
@Autowired
@Qualifier("proxied")
private Job proxied;
@Autowired
@Qualifier("nested")
private Job nested;
@Autowired
@Qualifier("enhanced")
private Job enhanced;
@Autowired
@Qualifier("double")
private Job doubleEnhanced;
@Before
@After
public void start() {
JobSynchronizationManager.close();
TestJob.reset();
}
@Test
public void testScopeCreation() throws Exception {
vanilla.execute(new JobExecution(11L));
assertNotNull(TestJob.getContext());
assertNull(JobSynchronizationManager.getContext());
}
@Test
public void testScopedProxy() throws Exception {
proxied.execute(new JobExecution(11L));
assertTrue(TestJob.getContext().attributeNames().length > 0);
String collaborator = (String) TestJob.getContext().getAttribute("collaborator");
assertNotNull(collaborator);
assertEquals("bar", collaborator);
assertTrue("Scoped proxy not created", ((String) TestJob.getContext().getAttribute("collaborator.class"))
.startsWith("class $Proxy"));
}
@Test
public void testNestedScopedProxy() throws Exception {
nested.execute(new JobExecution(11L));
assertTrue(TestJob.getContext().attributeNames().length > 0);
String collaborator = (String) TestJob.getContext().getAttribute("collaborator");
assertNotNull(collaborator);
assertEquals("foo", collaborator);
String parent = (String) TestJob.getContext().getAttribute("parent");
assertNotNull(parent);
assertEquals("bar", parent);
assertTrue("Scoped proxy not created", ((String) TestJob.getContext().getAttribute("parent.class"))
.startsWith("class $Proxy"));
}
@Test
public void testExecutionContext() throws Exception {
JobExecution stepExecution = new JobExecution(11L);
ExecutionContext executionContext = new ExecutionContext();
executionContext.put("name", "spam");
stepExecution.setExecutionContext(executionContext);
proxied.execute(stepExecution);
assertTrue(TestJob.getContext().attributeNames().length > 0);
String collaborator = (String) TestJob.getContext().getAttribute("collaborator");
assertNotNull(collaborator);
assertEquals("bar", collaborator);
}
@Test
public void testScopedProxyForReference() throws Exception {
enhanced.execute(new JobExecution(11L));
assertTrue(TestJob.getContext().attributeNames().length > 0);
String collaborator = (String) TestJob.getContext().getAttribute("collaborator");
assertNotNull(collaborator);
assertEquals("bar", collaborator);
}
@Test
public void testScopedProxyForSecondReference() throws Exception {
doubleEnhanced.execute(new JobExecution(11L));
assertTrue(TestJob.getContext().attributeNames().length > 0);
String collaborator = (String) TestJob.getContext().getAttribute("collaborator");
assertNotNull(collaborator);
assertEquals("bar", collaborator);
}
}

View File

@@ -0,0 +1,33 @@
package org.springframework.batch.core.scope;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.Job;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobScopeNestedIntegrationTests {
@Autowired
@Qualifier("proxied")
private Job proxied;
@Autowired
@Qualifier("parent")
private Collaborator parent;
@Test
public void testNestedScopedProxy() throws Exception {
assertNotNull(proxied);
assertEquals("foo", parent.getName());
}
}

View File

@@ -0,0 +1,155 @@
package org.springframework.batch.core.scope;
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobScopePlaceholderIntegrationTests implements BeanFactoryAware {
@Autowired
@Qualifier("simple")
private Collaborator simple;
@Autowired
@Qualifier("compound")
private Collaborator compound;
@Autowired
@Qualifier("value")
private Collaborator value;
@Autowired
@Qualifier("ref")
private Collaborator ref;
@Autowired
@Qualifier("scopedRef")
private Collaborator scopedRef;
@Autowired
@Qualifier("list")
private Collaborator list;
@Autowired
@Qualifier("bar")
private Collaborator bar;
@Autowired
@Qualifier("nested")
private Collaborator nested;
private JobExecution jobExecution;
private ListableBeanFactory beanFactory;
private int beanCount;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Before
public void start() {
start("bar");
}
private void start(String foo) {
JobSynchronizationManager.close();
jobExecution = new JobExecution(123L);
ExecutionContext executionContext = new ExecutionContext();
executionContext.put("foo", foo);
executionContext.put("parent", bar);
jobExecution.setExecutionContext(executionContext);
JobSynchronizationManager.register(jobExecution);
beanCount = beanFactory.getBeanDefinitionCount();
}
@After
public void stop() {
JobSynchronizationManager.close();
// Check that all temporary bean definitions are cleaned up
assertEquals(beanCount, beanFactory.getBeanDefinitionCount());
}
@Test
public void testSimpleProperty() throws Exception {
assertEquals("bar", simple.getName());
// Once the job context is set up it should be baked into the proxies
// so changing it now should have no effect
jobExecution.getExecutionContext().put("foo", "wrong!");
assertEquals("bar", simple.getName());
}
@Test
public void testCompoundProperty() throws Exception {
assertEquals("bar-bar", compound.getName());
}
@Test
public void testCompoundPropertyTwice() throws Exception {
assertEquals("bar-bar", compound.getName());
JobSynchronizationManager.close();
jobExecution = new JobExecution(123L);
ExecutionContext executionContext = new ExecutionContext();
executionContext.put("foo", "spam");
jobExecution.setExecutionContext(executionContext);
JobSynchronizationManager.register(jobExecution);
assertEquals("spam-bar", compound.getName());
}
@Test
public void testParentByRef() throws Exception {
assertEquals("bar", ref.getParent().getName());
}
@Test
public void testParentByValue() throws Exception {
assertEquals("bar", value.getParent().getName());
}
@Test
public void testList() throws Exception {
assertEquals("[bar]", list.getList().toString());
}
@Test
public void testNested() throws Exception {
assertEquals("bar", nested.getParent().getName());
}
@Test
public void testScopedRef() throws Exception {
assertEquals("bar", scopedRef.getParent().getName());
stop();
start("spam");
assertEquals("spam", scopedRef.getParent().getName());
}
}

View File

@@ -0,0 +1,71 @@
package org.springframework.batch.core.scope;
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobScopeProxyTargetClassIntegrationTests implements BeanFactoryAware {
@Autowired
@Qualifier("simple")
private TestCollaborator simple;
private JobExecution jobExecution;
private ListableBeanFactory beanFactory;
private int beanCount;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Before
public void start() {
JobSynchronizationManager.close();
jobExecution = new JobExecution(123L);
ExecutionContext executionContext = new ExecutionContext();
executionContext.put("foo", "bar");
jobExecution.setExecutionContext(executionContext);
JobSynchronizationManager.register(jobExecution);
beanCount = beanFactory.getBeanDefinitionCount();
}
@After
public void cleanUp() {
JobSynchronizationManager.close();
// Check that all temporary bean definitions are cleaned up
assertEquals(beanCount, beanFactory.getBeanDefinitionCount());
}
@Test
public void testSimpleProperty() throws Exception {
assertEquals("bar", simple.getName());
// Once the job context is set up it should be baked into the proxies
// so changing it now should have no effect
jobExecution.getExecutionContext().put("foo", "wrong!");
assertEquals("bar", simple.getName());
}
}

View File

@@ -0,0 +1,17 @@
package org.springframework.batch.core.scope;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobScopeStartupIntegrationTests {
@Test
public void testScopedProxyDuringStartup() throws Exception {
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.scope;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.scope.context.JobContext;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.support.StaticApplicationContext;
/**
* @author Dave Syer
* @author Jimmy Praet
*/
public class JobScopeTests {
private JobScope scope = new JobScope();
private JobExecution jobExecution = new JobExecution(0L);
private JobContext context;
@Before
public void setUp() throws Exception {
context = JobSynchronizationManager.register(jobExecution);
}
@After
public void tearDown() throws Exception {
JobSynchronizationManager.release();
}
@Test
public void testGetWithNoContext() throws Exception {
final String foo = "bar";
JobSynchronizationManager.release();
try {
scope.get("foo", new ObjectFactory() {
public Object getObject() throws BeansException {
return foo;
}
});
fail("Expected IllegalStateException");
}
catch (IllegalStateException e) {
// expected
}
}
@Test
public void testGetWithNothingAlreadyThere() {
final String foo = "bar";
Object value = scope.get("foo", new ObjectFactory() {
public Object getObject() throws BeansException {
return foo;
}
});
assertEquals(foo, value);
assertTrue(context.hasAttribute("foo"));
}
@Test
public void testGetWithSomethingAlreadyThere() {
context.setAttribute("foo", "bar");
Object value = scope.get("foo", new ObjectFactory() {
public Object getObject() throws BeansException {
return null;
}
});
assertEquals("bar", value);
assertTrue(context.hasAttribute("foo"));
}
@Test
public void testGetConversationId() {
String id = scope.getConversationId();
assertNotNull(id);
}
@Test
public void testRegisterDestructionCallback() {
final List<String> list = new ArrayList<String>();
context.setAttribute("foo", "bar");
scope.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("foo");
}
});
assertEquals(0, list.size());
// When the context is closed, provided the attribute exists the
// callback is called...
context.close();
assertEquals(1, list.size());
}
@Test
public void testRegisterAnotherDestructionCallback() {
final List<String> list = new ArrayList<String>();
context.setAttribute("foo", "bar");
scope.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("foo");
}
});
scope.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("bar");
}
});
assertEquals(0, list.size());
// When the context is closed, provided the attribute exists the
// callback is called...
context.close();
assertEquals(2, list.size());
}
@Test
public void testRemove() {
context.setAttribute("foo", "bar");
scope.remove("foo");
assertFalse(context.hasAttribute("foo"));
}
@Test
public void testOrder() throws Exception {
assertEquals(Integer.MAX_VALUE, scope.getOrder());
scope.setOrder(11);
assertEquals(11, scope.getOrder());
}
@Test
public void testName() throws Exception {
scope.setName("foo");
StaticApplicationContext beanFactory = new StaticApplicationContext();
scope.postProcessBeanFactory(beanFactory.getDefaultListableBeanFactory());
String[] scopes = beanFactory.getDefaultListableBeanFactory().getRegisteredScopeNames();
assertEquals(1, scopes.length);
assertEquals("foo", scopes[0]);
}
}

View File

@@ -1,22 +1,21 @@
package org.springframework.batch.core.scope;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.beans.factory.InitializingBean;
public class JobStartupRunner implements InitializingBean {
private Step step;
private Job job;
public void setStep(Step step) {
this.step = step;
public void setJob(Job job) {
this.job = job;
}
@Override
public void afterPropertiesSet() throws Exception {
StepExecution stepExecution = new StepExecution("step", new JobExecution(1L), 0L);
step.execute(stepExecution);
JobExecution jobExecution = new JobExecution(11L);
job.execute(jobExecution);
// expect no errors
}

View File

@@ -0,0 +1,22 @@
package org.springframework.batch.core.scope;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.beans.factory.InitializingBean;
public class StepStartupRunner implements InitializingBean {
private Step step;
public void setStep(Step step) {
this.step = step;
}
public void afterPropertiesSet() throws Exception {
StepExecution stepExecution = new StepExecution("step", new JobExecution(1L), 0L);
step.execute(stepExecution);
// expect no errors
}
}

View File

@@ -0,0 +1,61 @@
package org.springframework.batch.core.scope;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersValidator;
import org.springframework.batch.core.scope.context.JobContext;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
public class TestJob implements Job {
private static JobContext context;
private Collaborator collaborator;
public void setCollaborator(Collaborator collaborator) {
this.collaborator = collaborator;
}
public static JobContext getContext() {
return context;
}
public static void reset() {
context = null;
}
public void execute(JobExecution stepExecution) {
context = JobSynchronizationManager.getContext();
setContextFromCollaborator();
stepExecution.getExecutionContext().put("foo", "changed but it shouldn't affect the collaborator");
setContextFromCollaborator();
}
private void setContextFromCollaborator() {
if (context != null) {
context.setAttribute("collaborator", collaborator.getName());
context.setAttribute("collaborator.class", collaborator.getClass().toString());
if (collaborator.getParent()!=null) {
context.setAttribute("parent", collaborator.getParent().getName());
context.setAttribute("parent.class", collaborator.getParent().getClass().toString());
}
}
}
public String getName() {
return "foo";
}
public boolean isRestartable() {
return false;
}
public JobParametersIncrementer getJobParametersIncrementer() {
return null;
}
public JobParametersValidator getJobParametersValidator() {
return null;
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.scope.context;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.item.ExecutionContext;
/**
* @author Dave Syer
* @author Jimmy Praet
*/
public class JobContextTests {
private List<String> list = new ArrayList<String>();
private JobExecution jobExecution = new JobExecution(new JobInstance(2L, null, "job"), 1L);
private JobContext context = new JobContext(jobExecution);
@Test
public void testGetJobExecution() {
context = new JobContext(jobExecution);
assertNotNull(context.getJobExecution());
}
@Test
public void testNullJobExecution() {
try {
context = new JobContext(null);
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testEqualsSelf() {
assertEquals(context, context);
}
@Test
public void testNotEqualsNull() {
assertFalse(context.equals(null));
}
@Test
public void testEqualsContextWithSameJobExecution() {
assertEquals(new JobContext(jobExecution), context);
}
@Test
public void testDestructionCallbackSunnyDay() throws Exception {
context.setAttribute("foo", "FOO");
context.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("bar");
}
});
context.close();
assertEquals(1, list.size());
assertEquals("bar", list.get(0));
}
@Test
public void testDestructionCallbackMissingAttribute() throws Exception {
context.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("bar");
}
});
context.close();
// Yes the callback should be called even if the attribute is missing -
// for inner beans
assertEquals(1, list.size());
}
@Test
public void testDestructionCallbackWithException() throws Exception {
context.setAttribute("foo", "FOO");
context.setAttribute("bar", "BAR");
context.registerDestructionCallback("bar", new Runnable() {
public void run() {
list.add("spam");
throw new RuntimeException("fail!");
}
});
context.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("bar");
throw new RuntimeException("fail!");
}
});
try {
context.close();
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// We don't care which one was thrown...
assertEquals("fail!", e.getMessage());
}
// ...but we do care that both were executed:
assertEquals(2, list.size());
assertTrue(list.contains("bar"));
assertTrue(list.contains("spam"));
}
@Test
public void testJobName() throws Exception {
assertEquals("job", context.getJobName());
}
@Test
public void testJobExecutionContext() throws Exception {
ExecutionContext executionContext = jobExecution.getExecutionContext();
executionContext.put("foo", "bar");
assertEquals("bar", context.getJobExecutionContext().get("foo"));
}
@Test
public void testSystemProperties() throws Exception {
System.setProperty("foo", "bar");
assertEquals("bar", context.getSystemProperties().getProperty("foo"));
}
@Test
public void testJobParameters() throws Exception {
JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters();
JobInstance jobInstance = new JobInstance(0L, jobParameters, "foo");
jobExecution.setJobInstance(jobInstance);
assertEquals("bar", context.getJobParameters().get("foo"));
}
@Test
public void testContextId() throws Exception {
assertEquals("jobExecution#1", context.getId());
}
@Test(expected = IllegalStateException.class)
public void testIllegalContextId() throws Exception {
JobParameters jobParameters = new JobParametersBuilder().addString("foo", "bar").toJobParameters();
JobInstance jobInstance = new JobInstance(0L, jobParameters, "foo");
context = new JobContext(new JobExecution(jobInstance));
context.getId();
}
}

View File

@@ -0,0 +1,133 @@
package org.springframework.batch.core.scope.context;
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;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.util.ReflectionUtils;
/**
* JobSynchronizationManagerTests.
*
* @author Jimmy Praet
*/
public class JobSynchronizationManagerTests {
private JobExecution jobExecution = new JobExecution(0L);
@Before
@After
public void start() {
while (JobSynchronizationManager.getContext() != null) {
JobSynchronizationManager.close();
}
}
@Test
public void testGetContext() {
assertNull(JobSynchronizationManager.getContext());
JobSynchronizationManager.register(jobExecution);
assertNotNull(JobSynchronizationManager.getContext());
}
@Test
public void testClose() throws Exception {
final List<String> list = new ArrayList<String>();
JobContext context = JobSynchronizationManager.register(jobExecution);
context.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("foo");
}
});
JobSynchronizationManager.close();
assertNull(JobSynchronizationManager.getContext());
assertEquals(0, list.size());
// check for possible memory leak
assertEquals(0, extractStaticMap("counts").size());
assertEquals(0, extractStaticMap("contexts").size());
}
private Map<?, ?> extractStaticMap(String name) throws IllegalAccessException {
Field field = ReflectionUtils.findField(JobSynchronizationManager.class, "synchronizationManager");
ReflectionUtils.makeAccessible(field);
SynchronizationManagerSupport<?, ?> synchronizationManager =
(SynchronizationManagerSupport<?, ?>) field.get(JobSynchronizationManager.class);
field = ReflectionUtils.findField(SynchronizationManagerSupport.class, name);
ReflectionUtils.makeAccessible(field);
Map<?, ?> map = (Map<?, ?>) field.get(synchronizationManager);
return map;
}
@Test
public void testMultithreaded() throws Exception {
JobContext context = JobSynchronizationManager.register(jobExecution);
ExecutorService executorService = Executors.newFixedThreadPool(2);
FutureTask<JobContext> task = new FutureTask<JobContext>(new Callable<JobContext>() {
public JobContext call() throws Exception {
try {
JobSynchronizationManager.register(jobExecution);
JobContext context = JobSynchronizationManager.getContext();
context.setAttribute("foo", "bar");
return context;
}
finally {
JobSynchronizationManager.close();
}
}
});
executorService.execute(task);
executorService.awaitTermination(1, TimeUnit.SECONDS);
assertEquals(context.attributeNames().length, task.get().attributeNames().length);
JobSynchronizationManager.close();
assertNull(JobSynchronizationManager.getContext());
}
@Test
public void testRelease() {
JobContext context = JobSynchronizationManager.register(jobExecution);
final List<String> list = new ArrayList<String>();
context.registerDestructionCallback("foo", new Runnable() {
public void run() {
list.add("foo");
}
});
// On release we expect the destruction callbacks to be called
JobSynchronizationManager.release();
assertNull(JobSynchronizationManager.getContext());
assertEquals(1, list.size());
}
@Test
public void testRegisterNull() {
assertNull(JobSynchronizationManager.getContext());
JobSynchronizationManager.register(null);
assertNull(JobSynchronizationManager.getContext());
}
@Test
public void testRegisterTwice() {
JobSynchronizationManager.register(jobExecution);
JobSynchronizationManager.register(jobExecution);
JobSynchronizationManager.close();
// if someone registers you have to assume they are going to close, so
// the last thing you want is for the close to remove another context
// that someone else has registered
assertNotNull(JobSynchronizationManager.getContext());
JobSynchronizationManager.close();
assertNull(JobSynchronizationManager.getContext());
}
}

View File

@@ -86,9 +86,13 @@ public class StepSynchronizationManagerTests {
}
private Map<?, ?> extractStaticMap(String name) throws IllegalAccessException {
Field field = ReflectionUtils.findField(StepSynchronizationManager.class, name);
Field field = ReflectionUtils.findField(StepSynchronizationManager.class, "synchronizationManager");
ReflectionUtils.makeAccessible(field);
Map<?, ?> map = (Map<?, ?>) field.get(StepSynchronizationManager.class);
SynchronizationManagerSupport<?, ?> synchronizationManager =
(SynchronizationManagerSupport<?, ?>) field.get(StepSynchronizationManager.class);
field = ReflectionUtils.findField(SynchronizationManagerSupport.class, name);
ReflectionUtils.makeAccessible(field);
Map<?, ?> map = (Map<?, ?>) field.get(synchronizationManager);
return map;
}

View File

@@ -0,0 +1,38 @@
package org.springframework.batch.core.scope.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.After;
import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.scope.context.JobContext;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
public class JobContextFactoryTests {
private JobContextFactory factory = new JobContextFactory();
@After
public void cleanUp() {
JobSynchronizationManager.close();
JobSynchronizationManager.close();
}
@Test
public void testGetContext() {
JobExecution jobExecution = new JobExecution(11L);
JobContext context = JobSynchronizationManager.register(jobExecution);
assertEquals(context, factory.getContext());
}
@Test
public void testGetContextId() {
JobSynchronizationManager.register(new JobExecution(11L));
Object id1 = factory.getContextId();
JobSynchronizationManager.register(new JobExecution(12L));
Object id2 = factory.getContextId();
assertFalse(id2.equals(id1));
}
}