Package org.jbehave.core.embedder

Source Code of org.jbehave.core.embedder.StoryManager$StoryTimeout

package org.jbehave.core.embedder;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.embedder.StoryRunner.State;
import org.jbehave.core.failures.BatchFailures;
import org.jbehave.core.model.Story;
import org.jbehave.core.model.StoryDuration;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.StepCollector.Stage;

/**
* Manages the execution and outcomes of running stories. While each story is
* run by the {@link StoryRunner}, the manager is responsible for the concurrent
* submission and monitoring of their execution via the {@link ExecutorService}.
*/
public class StoryManager {

    private final Configuration configuration;
    private final EmbedderControls embedderControls;
    private final EmbedderMonitor embedderMonitor;
    private final ExecutorService executorService;
    private final InjectableStepsFactory stepsFactory;
    private final StoryRunner storyRunner;
    private final Map<String, RunningStory> runningStories = new HashMap<String, RunningStory>();
    private final Map<MetaFilter, List<Story>> excludedStories = new HashMap<MetaFilter, List<Story>>();
   
    public StoryManager(Configuration configuration, EmbedderControls embedderControls,
            EmbedderMonitor embedderMonitor, ExecutorService executorService, InjectableStepsFactory stepsFactory,
            StoryRunner storyRunner) {
        this.configuration = configuration;
        this.embedderControls = embedderControls;
        this.embedderMonitor = embedderMonitor;
        this.executorService = executorService;
        this.stepsFactory = stepsFactory;
        this.storyRunner = storyRunner;
    }

    public Story storyOfPath(String storyPath) {
        return storyRunner.storyOfPath(configuration, storyPath);
    }

    public Story storyOfText(String storyAsText, String storyId) {
        return storyRunner.storyOfText(configuration, storyAsText, storyId);
    }

    public void clear() {
        runningStories.clear();
    }

    public List<StoryOutcome> outcomes() {
        List<StoryOutcome> outcomes = new ArrayList<StoryOutcome>();
        for (RunningStory story : runningStories.values()) {
            outcomes.add(new StoryOutcome(story));
        }
        return outcomes;
    }
   
    public void runStories(List<String> storyPaths, MetaFilter filter, BatchFailures failures) {
        // configure cross reference with meta filter
        if ( configuration.storyReporterBuilder().hasCrossReference() ){
            configuration.storyReporterBuilder().crossReference().withMetaFilter(filter.asString());
        }
       
        // run before stories
        State beforeStories = runBeforeOrAfterStories(failures, Stage.BEFORE);

        // run stories as paths
        runningStoriesAsPaths(storyPaths, filter, beforeStories);
        waitUntilAllDoneOrFailed(failures);
        List<Story> notAllowed = notAllowedBy(filter);
        if (!notAllowed.isEmpty()) {
            embedderMonitor.storiesNotAllowed(notAllowed, filter, embedderControls.verboseFiltering());
        }

        // run after stories
       runBeforeOrAfterStories(failures, Stage.AFTER);
    }

    public State runBeforeOrAfterStories(BatchFailures failures, Stage stage) {
        List<CandidateSteps> candidateSteps = stepsFactory.createCandidateSteps();
        State state = storyRunner.runBeforeOrAfterStories(configuration, candidateSteps, stage);
        if (storyRunner.failed(state)) {
            failures.put(state.toString(), storyRunner.failure(state));
        }
        return state;
    }

    public Map<String, RunningStory> runningStoriesAsPaths(List<String> storyPaths, MetaFilter filter,
            State beforeStories) {
        for (String storyPath : storyPaths) {
            filterRunning(filter, beforeStories, storyPath, storyOfPath(storyPath));
        }
        return runningStories;
    }

    public Map<String, RunningStory> runningStories(List<Story> stories, MetaFilter filter, State beforeStories) {
        for (Story story : stories) {
            filterRunning(filter, beforeStories, story.getPath(), story);
        }
        return runningStories;
    }

    private void filterRunning(MetaFilter filter, State beforeStories, String storyPath, Story story) {
        FilteredStory filteredStory = new FilteredStory(filter, story, configuration.storyControls());
        if (filteredStory.allowed()) {
            runningStories.put(storyPath, runningStory(storyPath, story, filter, beforeStories));
        } else {
            notAllowedBy(filter).add(story);
        }
    }

    public List<Story> notAllowedBy(MetaFilter filter) {
        List<Story> stories = excludedStories.get(filter);
        if (stories == null) {
            stories = new ArrayList<Story>();
            excludedStories.put(filter, stories);
        }
        return stories;
    }

    public RunningStory runningStory(String storyPath, Story story, MetaFilter filter, State beforeStories) {
        return submit(new EnqueuedStory(storyRunner, configuration, stepsFactory, embedderControls, embedderMonitor,
                storyPath, story, filter, beforeStories));
    }

    public void waitUntilAllDoneOrFailed(BatchFailures failures) {
        if ( runningStories.values().isEmpty() ) {
          return;
        }
        boolean allDone = false;
        boolean started = false;
        while (!allDone || !started) {
            allDone = true;
            for (RunningStory runningStory : runningStories.values()) {             
                if ( runningStory.isStarted() ){
                  started = true;
                    Story story = runningStory.getStory();
          Future<ThrowableStory> future = runningStory.getFuture();
          if (!future.isDone()) {
            allDone = false;
            StoryDuration duration = runningStory.getDuration();
            runningStory.updateDuration();
            if (duration.timedOut()) {
              embedderMonitor.storyTimeout(story, duration);
              storyRunner.cancelStory(story, duration);
              future.cancel(true);
              if (embedderControls.failOnStoryTimeout()) {
                throw new StoryExecutionFailed(story.getPath(),
                    new StoryTimeout(duration));
              }
              continue;
            }
          } else {
            try {
              ThrowableStory throwableStory = future.get();
              Throwable throwable = throwableStory.getThrowable();
              if (throwable != null) {
                failures.put(story.getPath(), throwable);
                if (!embedderControls.ignoreFailureInStories()) {
                  continue;
                }
              }
            } catch (Throwable e) {
              failures.put(story.getPath(), e);
              if (!embedderControls.ignoreFailureInStories()) {
                continue;
              }
            }
          }
                } else {
                  started = false;
                }
            }
            tickTock();
        }
        // collect story durations and cancel any outstanding execution which is not done before returning
        Properties storyDurations = new Properties();
        long total = 0;
        for (RunningStory runningStory : runningStories.values()) {
          long durationInMillis = runningStory.getDurationInMillis();
          total += durationInMillis;
      storyDurations.setProperty(runningStory.getStory().getPath(), Long.toString(durationInMillis));
            Future<ThrowableStory> future = runningStory.getFuture();
            if (!future.isDone()) {
                future.cancel(true);
            }           
        }
        storyDurations.setProperty("total", Long.toString(total));
        write(storyDurations, "storyDurations.props");
    }

    private void write(Properties p, String name) {
        File outputDirectory = configuration.storyReporterBuilder().outputDirectory();
        try {
          Writer output = new FileWriter(new File(outputDirectory, name));
            p.store(output, this.getClass().getName());
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

  private void tickTock() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }

  private synchronized RunningStory submit(EnqueuedStory enqueuedStory) {
    return new RunningStory(enqueuedStory, executorService.submit(enqueuedStory));
  }

  private static class EnqueuedStory implements Callable<ThrowableStory> {
    private final StoryRunner storyRunner;
    private final Configuration configuration;
    private final InjectableStepsFactory stepsFactory;
    private final EmbedderControls embedderControls;
    private final EmbedderMonitor embedderMonitor;
    private final String storyPath;
    private final Story story;
    private final MetaFilter filter;
    private final State beforeStories;
    private long startedAtMillis;

    private EnqueuedStory(StoryRunner storyRunner,
        Configuration configuration,
        InjectableStepsFactory stepsFactory,
        EmbedderControls embedderControls,
        EmbedderMonitor embedderMonitor, String storyPath, Story story,
        MetaFilter filter, State beforeStories) {
      this.storyRunner = storyRunner;
      this.configuration = configuration;
      this.stepsFactory = stepsFactory;
      this.embedderControls = embedderControls;
      this.embedderMonitor = embedderMonitor;
      this.storyPath = storyPath;
      this.story = story;
      this.filter = filter;
      this.beforeStories = beforeStories;
    }

    public ThrowableStory call() throws Exception {
      try {
        embedderMonitor.runningStory(storyPath);
        startedAtMillis = System.currentTimeMillis();
        storyRunner.run(configuration, stepsFactory, story, filter, beforeStories);
      } catch (Throwable e) {
        if (embedderControls.ignoreFailureInStories()) {
          embedderMonitor.storyFailed(storyPath, e);
        } else {
          return new ThrowableStory(story, new StoryExecutionFailed(
              storyPath, e));
        }
      }
      return new ThrowableStory(story, null);
    }

    public Story getStory() {
      return story;
    }

    public long getStartedAtMillis() {
      return startedAtMillis;
    }

    public long getTimeoutInSecs() {
      return embedderControls.storyTimeoutInSecs();
    }
  }
 
    @SuppressWarnings("serial")
    public static class StoryExecutionFailed extends RuntimeException {

        public StoryExecutionFailed(String storyPath, Throwable failure) {
            super(storyPath, failure);
        }

    }

    @SuppressWarnings("serial")
    public static class StoryTimeout extends RuntimeException {

    public StoryTimeout(StoryDuration storyDuration) {
      super(storyDuration.getDurationInSecs() + "s > " + storyDuration.getTimeoutInSecs() + "s");
    }

    }

    public static class ThrowableStory {
        private Story story;
        private Throwable throwable;

        public ThrowableStory(Story story, Throwable throwable) {
            this.story = story;
            this.throwable = throwable;
        }

        public Story getStory() {
            return story;
        }

        public Throwable getThrowable() {
            return throwable;
        }
    }

  public static class RunningStory {
    private EnqueuedStory enqueuedStory;
    private Future<ThrowableStory> future;
    private StoryDuration duration;

    public RunningStory(EnqueuedStory enqueuedStory,
        Future<ThrowableStory> future) {
      this.enqueuedStory = enqueuedStory;
      this.future = future;
    }

    public Future<ThrowableStory> getFuture() {
      return future;
    }

    public Story getStory() {
      return enqueuedStory.getStory();
    }

    public long getDurationInMillis() {
      if ( duration == null ){
        return 0;
      }
      return duration.getDurationInSecs() * 1000;
    }

    public StoryDuration getDuration() {
      if (duration == null) {
        duration = new StoryDuration(enqueuedStory.getStartedAtMillis(), enqueuedStory.getTimeoutInSecs());
      }
      return duration;
    }

    public void updateDuration() {
      duration.update();
    }

    public boolean isStarted() {
      long startedAtMillis = enqueuedStory.getStartedAtMillis();
      return startedAtMillis != 0;
    }

    public boolean isDone() {
      return future.isDone();
    }

    public boolean isFailed() {
      if (isDone()) {
        try {
          return future.get().getThrowable() != null;
        } catch (InterruptedException e) {
        } catch (ExecutionException e) {
        }
      }
      return false;
    }
  }
 
    public static class StoryOutcome {
        private String path;
        private Boolean done;
        private Boolean failed;

        public StoryOutcome(RunningStory story) {
            this.path = story.getStory().getPath();
            this.done = story.isDone();
            this.failed = story.isFailed();
        }

        public String getPath() {
            return path;
        }

        public Boolean isDone() {
            return done;
        }

        public Boolean isFailed() {
            return failed;
        }

    }

}
TOP

Related Classes of org.jbehave.core.embedder.StoryManager$StoryTimeout

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.