[长描述警告]
我正在运行一些必须执行插入已定义服务器的黄瓜测试 - 例如: a.feature - > JBoss Server 1 | b.feature - > JBoss Serv。 2 | c.feature - > JB1 |等
为此,我创建了一个假设的ExecutorService,如下所示:
final ExecutorService executorService = Executors.newFixedThreadPool(2); //numberOfServers
for (Runnable task : tasks) {
executorService.execute(task);
}
executorService.shutdown();
try {
executorService.awaitTermination(1000, TimeUnit.SECONDS);
} catch (InterruptedException e) {
//doX();
}
我管理如何选择可以执行的服务器的方式是:
在为executorService创建的Runnable类中,我将 instanceId 作为参数传递给TestNG(XmlTest类),如下所示:
@Override
public void run() {
setupTest().run();
}
private TestNG setupTest() {
TestNG testNG = new TestNG();
XmlSuite xmlSuite = new XmlSuite();
XmlTest xmlTest = new XmlTest(xmlSuite);
xmlTest.setName(//irrelevant);
xmlTest.addParameter("instanceId", String.valueOf(instanceId));
xmlTest.setXmlClasses(..........);
testNG.setXmlSuites(..........);
return testNG;
}
然后,我在 扩展TestNgCucumberAdaptor 的类中得到了这个:
@BeforeTest
@Parameters({"instanceId"})
public void setInstanceId(@Optional("") String instanceId) {
if (!StringUtils.isEmpty(instanceId)) {
this.instanceId = Integer.valueOf(instanceId);
}
}
在@BeforeClass中我用这个instanceId填充Pojo,并在另一个类的threadLocal属性中设置Pojo。到目前为止,非常好。
public class CurrentPojoContext {
private static final ThreadLocal<PojoContext> TEST_CONTEXT = new ThreadLocal<PojoContext>();
...
public static PojoContext getContext(){
TEST_CONTEXT.get();
}
现在问题真的开始了 - 我在第三类中使用Guice(Cucumber guice),注入包含instanceId的这个pojo对象。例子如下:
public class Environment {
protected final PojoContext pojoContext;
@Inject
public Environment() {
this.pojoContext = CurrentPojoContext.getContext();
}
public void foo() {
print(pojoContext.instanceId); // output: 1
Another.doSomething(pojoContext);
}
class Another{
public String doSomething(PojoContext p){
print(p.instanceId); // output: 2
}
}
}
这里不是每次这样的输出(1和2),但有时,我意识到不同线程的执行正在弄乱属性pojoContext。我知道这有点令人困惑,但我的猜测是Guice Injector在这种情况下不是线程安全的 - 它可能是一个很长的镜头,但如果其他人猜测,我会很感激。
此致
答案 0 :(得分:1)
嗯,只是为了给别人提供解决方案,我的解决方案如下:
在我的Guice注入器实例化中,我创建了自己的模块
Guice.createInjector(Stage.PRODUCTION, MyOwnModules.SCENARIO, new RandomModule());
并为此模块:
public class MyOwnModules {
public static final Module SCENARIO = new ScenarioModule(MyOwnCucumberScopes.SCENARIO);
}
此处定义的范围提供以下内容:
public class MyOwnCucumberScopes {
public static final ScenarioScope SCENARIO = new ParallelScenarioScope();
}
总之,线程安全将在ParallelScenarioScope中:
public class ParallelScenarioScope implements ScenarioScope {
private static final Logger LOGGER = Logger.getLogger(ParallelScenarioScope.class);
private final ThreadLocal<Map<Key<?>, Object>> threadLocalMap = new ThreadLocal<Map<Key<?>, Object>>();
@Override
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
return new Provider<T>() {
public T get() {
Map<Key<?>, Object> scopedObjects = getScopedObjectMap(key);
@SuppressWarnings("unchecked")
T current = (T) scopedObjects.get(key);
if (current == null && !scopedObjects.containsKey(key)) {
current = unscoped.get();
scopedObjects.put(key, current);
}
return current;
}
};
}
protected <T> Map<Key<?>, Object> getScopedObjectMap(Key<T> key) {
Map<Key<?>, Object> map = threadLocalMap.get();
if (map == null) {
throw new OutOfScopeException("Cannot access " + key + " outside of a scoping block");
}
return map;
}
@Override
public void enterScope() {
checkState(threadLocalMap.get() == null, "A scoping block is already in progress");
threadLocalMap.set(new ConcurrentHashMap<Key<?>, Object>());
}
@Override
public void exitScope() {
checkState(threadLocalMap.get() != null, "No scoping block in progress");
threadLocalMap.remove();
}
private void checkState(boolean expression, String errorMessage) {
if (!expression) {
LOGGER.info("M=checkState, Will throw exception: " + errorMessage);
throw new IllegalStateException(errorMessage);
}
}
}
现在问题就是@ScenarioScoped要小心,代码将按预期工作。