package org.jbehave.core.reporters;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.jbehave.core.model.ExamplesTable;
import org.jbehave.core.model.GivenStories;
import org.jbehave.core.model.Lifecycle;
import org.jbehave.core.model.Meta;
import org.jbehave.core.model.Narrative;
import org.jbehave.core.model.OutcomesTable;
import org.jbehave.core.model.Scenario;
import org.jbehave.core.model.Story;
import org.jbehave.core.model.StoryDuration;
/**
* When running a multithreading mode, reports cannot be written concurrently but should
* be delayed and invoked only at the end of a story, ensuring synchronization on the delegate
* responsible for the reporting.
*/
public class ConcurrentStoryReporter implements StoryReporter {
private static Method storyCancelled;
private static Method storyNotAllowed;
private static Method beforeStory;
private static Method afterStory;
private static Method narrative;
private static Method lifecycle;
private static Method scenarioNotAllowed;
private static Method beforeScenario;
private static Method scenarioMeta;
private static Method afterScenario;
private static Method givenStories;
private static Method givenStoriesPaths;
private static Method beforeExamples;
private static Method example;
private static Method afterExamples;
private static Method beforeStep;
private static Method successful;
private static Method ignorable;
private static Method comment;
private static Method pending;
private static Method notPerformed;
private static Method failed;
private static Method failedOutcomes;
private static Method dryRun;
private static Method pendingMethods;
private static Method restarted;
private static Method restartedStory;
static {
try {
storyCancelled = StoryReporter.class.getMethod("storyCancelled", Story.class, StoryDuration.class);
storyNotAllowed = StoryReporter.class.getMethod("storyNotAllowed", Story.class, String.class);
beforeStory = StoryReporter.class.getMethod("beforeStory", Story.class, Boolean.TYPE);
afterStory = StoryReporter.class.getMethod("afterStory", Boolean.TYPE);
narrative = StoryReporter.class.getMethod("narrative", Narrative.class);
lifecycle = StoryReporter.class.getMethod("lifecyle", Lifecycle.class);
scenarioNotAllowed = StoryReporter.class.getMethod("scenarioNotAllowed", Scenario.class, String.class);
beforeScenario = StoryReporter.class.getMethod("beforeScenario", String.class);
scenarioMeta = StoryReporter.class.getMethod("scenarioMeta", Meta.class);
afterScenario = StoryReporter.class.getMethod("afterScenario");
givenStories = StoryReporter.class.getMethod("givenStories", GivenStories.class);
givenStoriesPaths = StoryReporter.class.getMethod("givenStories", List.class);
beforeExamples = StoryReporter.class.getMethod("beforeExamples", List.class, ExamplesTable.class);
example = StoryReporter.class.getMethod("example", Map.class);
afterExamples = StoryReporter.class.getMethod("afterExamples");
beforeStep = StoryReporter.class.getMethod("beforeStep", String.class);
successful = StoryReporter.class.getMethod("successful", String.class);
ignorable = StoryReporter.class.getMethod("ignorable", String.class);
comment = StoryReporter.class.getMethod("comment", String.class);
pending = StoryReporter.class.getMethod("pending", String.class);
notPerformed = StoryReporter.class.getMethod("notPerformed", String.class);
failed = StoryReporter.class.getMethod("failed", String.class, Throwable.class);
failedOutcomes = StoryReporter.class.getMethod("failedOutcomes", String.class, OutcomesTable.class);
dryRun = StoryReporter.class.getMethod("dryRun");
pendingMethods = StoryReporter.class.getMethod("pendingMethods", List.class);
restarted = StoryReporter.class.getMethod("restarted", String.class, Throwable.class);
restartedStory = StoryReporter.class.getMethod("restartedStory", Story.class, Throwable.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private List<DelayedMethod> delayedMethods = new ArrayList<DelayedMethod>();
private final StoryReporter crossReferencing;
private final StoryReporter delegate;
private final boolean multiThreading;
private boolean invoked = false;
public ConcurrentStoryReporter(StoryReporter crossReferencing, StoryReporter delegate, boolean multiThreading) {
this.crossReferencing = crossReferencing;
this.multiThreading = multiThreading;
this.delegate = delegate;
}
@Override
public void storyNotAllowed(Story story, String filter) {
crossReferencing.storyNotAllowed(story, filter);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(storyNotAllowed, story, filter));
} else {
delegate.storyNotAllowed(story, filter);
}
}
@Override
public void beforeStory(Story story, boolean givenStory) {
crossReferencing.beforeStory(story, givenStory);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(beforeStory, story, givenStory));
} else {
delegate.beforeStory(story, givenStory);
}
}
@Override
public void afterStory(boolean givenStory) {
crossReferencing.afterStory(givenStory);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(afterStory, givenStory));
} else {
delegate.afterStory(givenStory);
}
}
@Override
public void narrative(Narrative aNarrative) {
crossReferencing.narrative(aNarrative);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(narrative, aNarrative));
} else {
delegate.narrative(aNarrative);
}
}
@Override
public void lifecyle(Lifecycle aLifecycle) {
crossReferencing.lifecyle(aLifecycle);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(lifecycle, aLifecycle));
} else {
delegate.lifecyle(aLifecycle);
}
}
@Override
public void scenarioNotAllowed(Scenario scenario, String filter) {
crossReferencing.scenarioNotAllowed(scenario, filter);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(scenarioNotAllowed, scenario, filter));
} else {
delegate.scenarioNotAllowed(scenario, filter);
}
}
@Override
public void beforeScenario(String scenarioTitle) {
crossReferencing.beforeScenario(scenarioTitle);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(beforeScenario, scenarioTitle));
} else {
delegate.beforeScenario(scenarioTitle);
}
}
@Override
public void scenarioMeta(Meta meta) {
crossReferencing.scenarioMeta(meta);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(scenarioMeta, meta));
} else {
delegate.scenarioMeta(meta);
}
}
@Override
public void afterScenario() {
crossReferencing.afterScenario();
if (multiThreading) {
delayedMethods.add(new DelayedMethod(afterScenario));
} else {
delegate.afterScenario();
}
}
@Override
public void givenStories(GivenStories stories) {
crossReferencing.givenStories(stories);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(givenStories, stories));
} else {
delegate.givenStories(stories);
}
}
@Override
public void givenStories(List<String> storyPaths) {
crossReferencing.givenStories(storyPaths);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(givenStoriesPaths, storyPaths));
} else {
delegate.givenStories(storyPaths);
}
}
@Override
public void beforeExamples(List<String> steps, ExamplesTable table) {
crossReferencing.beforeExamples(steps, table);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(beforeExamples, steps, table));
} else {
delegate.beforeExamples(steps, table);
}
}
@Override
public void example(Map<String, String> tableRow) {
crossReferencing.example(tableRow);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(example, tableRow));
} else {
delegate.example(tableRow);
}
}
@Override
public void afterExamples() {
crossReferencing.afterExamples();
if (multiThreading) {
delayedMethods.add(new DelayedMethod(afterExamples));
} else {
delegate.afterExamples();
}
}
@Override
public void beforeStep(String step) {
crossReferencing.beforeStep(step);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(beforeStep, step));
} else {
delegate.beforeStep(step);
}
}
@Override
public void successful(String step) {
crossReferencing.successful(step);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(successful, step));
} else {
delegate.successful(step);
}
}
@Override
public void ignorable(String step) {
crossReferencing.ignorable(step);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(ignorable, step));
} else {
delegate.ignorable(step);
}
}
@Override
public void comment(String step) {
crossReferencing.comment(step);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(comment, step));
} else {
delegate.comment(step);
}
}
@Override
public void pending(String step) {
crossReferencing.pending(step);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(pending, step));
} else {
delegate.pending(step);
}
}
@Override
public void notPerformed(String step) {
crossReferencing.notPerformed(step);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(notPerformed, step));
} else {
delegate.notPerformed(step);
}
}
@Override
public void failed(String step, Throwable cause) {
crossReferencing.failed(step, cause);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(failed, step, cause));
} else {
delegate.failed(step, cause);
}
}
@Override
public void failedOutcomes(String step, OutcomesTable table) {
crossReferencing.failedOutcomes(step, table);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(failedOutcomes, step, table));
} else {
delegate.failedOutcomes(step, table);
}
}
@Override
public void dryRun() {
crossReferencing.dryRun();
if (multiThreading) {
delayedMethods.add(new DelayedMethod(dryRun));
} else {
delegate.dryRun();
}
}
@Override
public void pendingMethods(List<String> methods) {
crossReferencing.pendingMethods(methods);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(pendingMethods, methods));
} else {
delegate.pendingMethods(methods);
}
}
@Override
public void restarted(String step, Throwable cause) {
crossReferencing.restarted(step, cause);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(restarted, step, cause));
} else {
delegate.restarted(step, cause);
}
}
@Override
public void restartedStory(Story story, Throwable cause){
crossReferencing.restartedStory(story, cause);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(restartedStory, story, cause));
} else {
delegate.restartedStory(story, cause);
}
}
@Override
public void storyCancelled(Story story, StoryDuration storyDuration) {
crossReferencing.storyCancelled(story, storyDuration);
if (multiThreading) {
delayedMethods.add(new DelayedMethod(storyCancelled, story, storyDuration));
} else {
delegate.storyCancelled(story, storyDuration);
}
}
public StoryReporter getDelegate() {
return delegate;
}
public boolean invoked(){
return invoked;
}
public void invokeDelayed() {
if ( !multiThreading ){
return;
}
synchronized (delegate) {
for (DelayedMethod delayed : Collections.unmodifiableList(delayedMethods)) {
delayed.invoke(delegate);
}
}
invoked = true;
}
public static class DelayedMethod {
private Method method;
private Object[] args;
public DelayedMethod(Method method, Object... args) {
this.method = method;
this.args = args;
}
public void invoke(StoryReporter delegate) {
try {
method.invoke(delegate, args);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException("" + method, e);
}
}
}
}