Cucumber Guice / Injector 似乎不是线程安全的(并行执行/执行器服务)



[详细描述警告]

我正在运行一些黄瓜测试,这些测试必须在定义的服务器中执行 - 例如: a.功能 -> JBoss 服务器 1 |b.功能 -> JBoss 服务2 |c.功能 -> JB1 |等。

为此,我创建了一个假设的执行器服务,如下所示:

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();
}

现在问题真正开始了 - 我在 3rd 类中使用 Guice(黄瓜 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 注入器在这种情况下不是线程安全的 - 这可能是一个很长的机会,但如果其他人猜测,我将不胜感激。

问候

好吧,只是为了给别人提供解决方案,我的解决方案如下:

  1. 创建一个类来维护一个 Map,其中标识符(唯一且线程安全的标识符)作为键,Guice 注入器作为值;
  2. 在我的 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();
    }
    
  3. 总而言之,线程安全将在 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,代码将按预期工作。

最新更新