Use InheritedThreadLocal (as interim measure) so that child threads are aware of step context

This commit is contained in:
dsyer
2007-12-12 22:53:32 +00:00
parent a90ae4a5bb
commit 2a648348ac
4 changed files with 103 additions and 12 deletions

View File

@@ -30,11 +30,12 @@ import org.springframework.core.Ordered;
* @author Dave Syer
*
*/
public class StepScope implements Scope,
BeanFactoryPostProcessor, Ordered {
public class StepScope implements Scope, BeanFactoryPostProcessor, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE;
private Object mutex = new Object();
public void setOrder(int order) {
this.order = order;
}
@@ -59,12 +60,16 @@ public class StepScope implements Scope,
SimpleStepContext context = getContext();
Object scopedObject = context.getAttribute(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
context.setAttribute(name, scopedObject);
if (scopedObject instanceof StepContextAware) {
((StepContextAware) scopedObject).setStepContext(context);
synchronized (mutex) {
scopedObject = context.getAttribute(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
context.setAttribute(name, scopedObject);
if (scopedObject instanceof StepContextAware) {
((StepContextAware) scopedObject).setStepContext(context);
}
}
}
}
return scopedObject;
}
@@ -132,7 +137,7 @@ public class StepScope implements Scope,
/**
* Public setter for the name property. This can then be used as a bean
* definition attribute, e.g. scope="step". Defaults to "step".
* definition attribute, e.g. scope="step". Defaults to "step".
*
* @param name
* the name to set for this scope.

View File

@@ -21,13 +21,13 @@ package org.springframework.batch.execution.scope;
*/
public class StepSynchronizationManager {
private static final ThreadLocal contextHolder = new ThreadLocal();
private static final ThreadLocal contextHolder = new InheritableThreadLocal();
/**
* Getter for the current context..
*
* @return the current {@link SimpleStepContext} or null if there is none (if
* we are not in a step).
* @return the current {@link SimpleStepContext} or null if there is none
* (if we are not in a step).
*/
public static SimpleStepContext getContext() {
return (SimpleStepContext) contextHolder.get();
@@ -56,7 +56,7 @@ public class StepSynchronizationManager {
*/
public static StepContext close() {
SimpleStepContext oldSession = getContext();
if (oldSession==null) {
if (oldSession == null) {
return null;
}
oldSession.close();

View File

@@ -77,12 +77,84 @@ public class StepContextAwareStepScopeTests extends TestCase {
assertEquals(1, list.size());
}
public void testScopedBeanWithProxy() throws Exception {
StepContext context = StepSynchronizationManager.open();
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("scope-tests.xml", getClass());
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
assertNotNull(bean);
// A scoped proxy is only accessible through public methods
assertEquals(null, bean.name);
assertEquals("spam", bean.getName());
assertEquals(context, bean.getContext());
}
public void testScopedBeanWithProxyInThread() throws Exception {
StepSynchronizationManager.open();
final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("scope-tests.xml", getClass());
new Thread(new Runnable() {
public void run() {
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
list.add(bean.getName());
}
}).start();
int count = 0;
while(list.size()==0 && count++ <10) {
Thread.sleep(100);
}
if (list.size()==0) {
fail("Scoped proxy was not created in child thread - maybe we need to use InheritableThreadLocal?");
}
String name = (String) list.get(0);
assertEquals("spam", name);
}
public void testScopedBeanWithTwoProxiesInThreads() throws Exception {
StepSynchronizationManager.open();
final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("scope-tests.xml", getClass());
new Thread(new Runnable() {
public void run() {
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
int count = 0;
while(list.size()==0 && count++ <10) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
fail("Timeout waiting for other thread to add a bean to list.");
}
}
bean.getName();
list.add(bean);
}
}).start();
new Thread(new Runnable() {
public void run() {
TestBeanAware bean = (TestBeanAware) applicationContext.getBean("proxy");
bean.getName();
list.add(bean);
}
}).start();
int count = 0;
while(list.size()<2 && count++ <10) {
Thread.sleep(100);
}
if (list.size()<2) {
fail("Scoped proxies were not created in child threads");
}
TestBeanAware bean1 = (TestBeanAware) list.get(0);
TestBeanAware bean2 = (TestBeanAware) list.get(1);
assertEquals("spam", bean1.getName());
assertSame(bean1.getLock(), bean2.getLock());
}
public static class TestBean {
String name;
TestBean child;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setChild(TestBean child) {
this.child = child;
}
@@ -93,8 +165,15 @@ public class StepContextAwareStepScopeTests extends TestCase {
public static class TestBeanAware extends TestBean implements StepContextAware {
AttributeAccessor context;
Object lock = new Object();
public void setStepContext(StepContext context) {
this.context = context;
}
public AttributeAccessor getContext() {
return context;
}
public Object getLock() {
return lock;
}
}
}

View File

@@ -36,4 +36,11 @@
<property name="name" value="bar" />
</bean>
<bean id="proxy"
class="org.springframework.batch.execution.scope.StepContextAwareStepScopeTests$TestBeanAware"
scope="step">
<aop:scoped-proxy/>
<property name="name" value="spam" />
</bean>
</beans>