AnnotationBuilder.java
- package org.jbehave.core.configuration;
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.lang.reflect.Constructor;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Properties;
- import com.thoughtworks.paranamer.Paranamer;
- import org.apache.commons.lang3.StringUtils;
- import org.jbehave.core.ConfigurableEmbedder;
- import org.jbehave.core.Embeddable;
- import org.jbehave.core.annotations.Configure;
- import org.jbehave.core.annotations.UsingEmbedder;
- import org.jbehave.core.annotations.UsingPaths;
- import org.jbehave.core.annotations.UsingSteps;
- import org.jbehave.core.embedder.Embedder;
- import org.jbehave.core.embedder.EmbedderControls;
- import org.jbehave.core.embedder.StoryControls;
- import org.jbehave.core.failures.FailureStrategy;
- import org.jbehave.core.failures.PendingStepStrategy;
- import org.jbehave.core.io.PathCalculator;
- import org.jbehave.core.io.ResourceLoader;
- import org.jbehave.core.io.StoryFinder;
- import org.jbehave.core.io.StoryLoader;
- import org.jbehave.core.io.StoryPathResolver;
- import org.jbehave.core.model.TableTransformers;
- import org.jbehave.core.parsers.StepPatternParser;
- import org.jbehave.core.parsers.StoryParser;
- import org.jbehave.core.reporters.StepdocReporter;
- import org.jbehave.core.reporters.StoryReporter;
- import org.jbehave.core.reporters.StoryReporterBuilder;
- import org.jbehave.core.reporters.ViewGenerator;
- import org.jbehave.core.steps.CandidateSteps;
- import org.jbehave.core.steps.InjectableStepsFactory;
- import org.jbehave.core.steps.InstanceStepsFactory;
- import org.jbehave.core.steps.ParameterControls;
- import org.jbehave.core.steps.ParameterConverters;
- import org.jbehave.core.steps.ParameterConverters.ParameterConverter;
- import org.jbehave.core.steps.ScanningStepsFactory;
- import org.jbehave.core.steps.StepCollector;
- import org.jbehave.core.steps.StepFinder;
- import org.jbehave.core.steps.StepMonitor;
- /**
- * Allows the building of {@link Configuration}, {@link CandidateSteps} and
- * {@link Embedder} from an annotated class.
- *
- * @author Cristiano GaviĆ£o
- * @author Mauro Talevi
- */
- public class AnnotationBuilder {
- private final AnnotationMonitor annotationMonitor;
- private final Class<?> annotatedClass;
- private final AnnotationFinder finder;
- public AnnotationBuilder(Class<?> annotatedClass) {
- this(annotatedClass, new PrintStreamAnnotationMonitor());
- }
- public AnnotationBuilder(Class<?> annotatedClass, AnnotationMonitor annotationMonitor) {
- this.annotationMonitor = annotationMonitor;
- this.annotatedClass = annotatedClass;
- this.finder = new AnnotationFinder(annotatedClass);
- }
- public Class<?> annotatedClass() {
- return annotatedClass;
- }
- /**
- * Builds a Configuration instance based on annotation {@link Configure}
- * found in the annotated object instance
- *
- * @return A Configuration instance
- */
- public Configuration buildConfiguration() throws AnnotationRequired {
- if (!finder.isAnnotationPresent(Configure.class)) {
- // not using annotation configuration, default to most useful
- // configuration
- return new MostUsefulConfiguration();
- }
- Configuration configuration = configurationElement(finder, "using", Configuration.class);
- configuration.useKeywords(configurationElement(finder, "keywords", Keywords.class));
- configuration.useFailureStrategy(configurationElement(finder, "failureStrategy", FailureStrategy.class));
- configuration.usePendingStepStrategy(configurationElement(finder, "pendingStepStrategy",
- PendingStepStrategy.class));
- configuration.useParanamer(configurationElement(finder, "paranamer", Paranamer.class));
- configuration.useStoryControls(configurationElement(finder, "storyControls", StoryControls.class));
- configuration.useStepCollector(configurationElement(finder, "stepCollector", StepCollector.class));
- configuration.useStepdocReporter(configurationElement(finder, "stepdocReporter", StepdocReporter.class));
- configuration.useStepFinder(configurationElement(finder, "stepFinder", StepFinder.class));
- configuration.useStepMonitor(configurationElement(finder, "stepMonitor", StepMonitor.class));
- configuration.useStepPatternParser(configurationElement(finder, "stepPatternParser", StepPatternParser.class));
- configuration.useStoryLoader(configurationElement(finder, "storyLoader", StoryLoader.class));
- configuration.useStoryParser(configurationElement(finder, "storyParser", StoryParser.class));
- configuration.useStoryPathResolver(configurationElement(finder, "storyPathResolver", StoryPathResolver.class));
- configuration
- .useDefaultStoryReporter(configurationElement(finder, "defaultStoryReporter", StoryReporter.class));
- configuration.useStoryReporterBuilder(configurationElement(finder, "storyReporterBuilder",
- StoryReporterBuilder.class));
- configuration.useViewGenerator(configurationElement(finder, "viewGenerator", ViewGenerator.class));
- configuration.useTableTransformers(configurationElement(finder, "tableTransformers", TableTransformers.class));
- configuration.useParameterConverters(
- parameterConverters(finder, configuration.storyLoader(), configuration.tableTransformers()));
- configuration.useParameterControls(configurationElement(finder, "parameterControls", ParameterControls.class));
- configuration.usePathCalculator(configurationElement(finder, "pathCalculator", PathCalculator.class));
- return configuration;
- }
- /**
- * Builds CandidateSteps using annotation {@link UsingSteps} found in the
- * annotated object instance and using the configuration build by
- * {@link #buildConfiguration()}
- *
- * @return A List of CandidateSteps instances
- */
- public List<CandidateSteps> buildCandidateSteps() {
- return buildCandidateSteps(buildConfiguration());
- }
- /**
- * Builds CandidateSteps using annotation {@link UsingSteps} found in the
- * annotated object instance and the configuration provided
- *
- * @param configuration the Configuration
- * @return A List of CandidateSteps instances
- */
- public List<CandidateSteps> buildCandidateSteps(Configuration configuration) {
- return buildStepsFactory(configuration).createCandidateSteps();
- }
- /**
- * Builds the {@link InjectableStepsFactory} using annotation
- * {@link UsingSteps} found in the annotated object instance and the
- * configuration provided
- *
- * @param configuration the Configuration
- * @return A {@link InjectableStepsFactory}
- */
- public InjectableStepsFactory buildStepsFactory(Configuration configuration) {
- List<Object> stepsInstances = new ArrayList<>();
- InjectableStepsFactory factory = null;
- if (finder.isAnnotationPresent(UsingSteps.class)) {
- List<Class<Object>> stepsClasses = finder.getAnnotatedClasses(
- UsingSteps.class, Object.class, "instances");
- if (!stepsClasses.isEmpty()) {
- for (Class<Object> stepsClass : stepsClasses) {
- stepsInstances.add(instanceOf(Object.class, stepsClass));
- }
- factory = new InstanceStepsFactory(configuration,
- stepsInstances);
- }
- List<String> packages = finder.getAnnotatedValues(UsingSteps.class,
- String.class, "packages");
- if (!packages.isEmpty()) {
- String matchingNames = finder.getAnnotatedValue(UsingSteps.class,
- String.class, "matchingNames");
- String notMatchingNames = finder.getAnnotatedValue(UsingSteps.class,
- String.class, "notMatchingNames");
- factory = new ScanningStepsFactory(configuration,
- packages.toArray(new String[packages.size()]))
- .matchingNames(matchingNames).notMatchingNames(notMatchingNames);
- }
- } else {
- annotationMonitor.annotationNotFound(UsingSteps.class, annotatedClass);
- }
- if (factory == null) {
- factory = new InstanceStepsFactory(configuration);
- }
- return factory;
- }
- public Embedder buildEmbedder() {
- if (!finder.isAnnotationPresent(UsingEmbedder.class)) {
- return defaultEmbedder();
- }
- boolean batch = control(finder, "batch");
- boolean skip = control(finder, "skip");
- boolean generateViewAfterStories = control(finder, "generateViewAfterStories");
- boolean ignoreFailureInStories = control(finder, "ignoreFailureInStories");
- boolean ignoreFailureInView = control(finder, "ignoreFailureInView");
- boolean verboseFailures = control(finder, "verboseFailures");
- boolean verboseFiltering = control(finder, "verboseFiltering");
- String storyTimeouts = finder.getAnnotatedValue(UsingEmbedder.class, String.class, "storyTimeouts");
- boolean failOnStoryTimeout = control(finder, "failOnStoryTimeout");
- int threads = finder.getAnnotatedValue(UsingEmbedder.class, Integer.class, "threads");
- Embedder embedder = embedder();
- EmbedderControls embedderControls = embedder.embedderControls();
- embedderControls.doBatch(batch).doSkip(skip).doGenerateViewAfterStories(generateViewAfterStories)
- .doIgnoreFailureInStories(ignoreFailureInStories).doIgnoreFailureInView(ignoreFailureInView)
- .doVerboseFailures(verboseFailures).doVerboseFiltering(verboseFiltering)
- .doFailOnStoryTimeout(failOnStoryTimeout).useThreads(threads);
- if (StringUtils.isNotBlank(storyTimeouts)) {
- embedderControls.useStoryTimeouts(storyTimeouts);
- }
- Configuration configuration = buildConfiguration();
- embedder.useConfiguration(configuration);
- embedder.useStepsFactory(buildStepsFactory(configuration));
- List<String> metaFilters = finder.getAnnotatedValues(UsingEmbedder.class, String.class, "metaFilters");
- if (!metaFilters.isEmpty()) {
- embedder.useMetaFilters(metaFilters);
- }
- Properties systemProperties = loadProperties(finder.getAnnotatedValue(UsingEmbedder.class, String.class,
- "systemProperties"));
- if (!systemProperties.isEmpty()) {
- embedder.useSystemProperties(systemProperties);
- }
- return embedder;
- }
- @SuppressWarnings("unchecked")
- protected Embedder embedder() {
- return instanceOf(Embedder.class,
- (Class<? extends Embedder>) finder.getAnnotatedValue(UsingEmbedder.class, Class.class, "embedder"));
- }
-
- protected Embedder defaultEmbedder() {
- return new Embedder();
- }
- public AnnotationFinder finder() {
- return finder;
- }
-
- public List<String> findPaths() {
- if (!finder.isAnnotationPresent(UsingPaths.class)) {
- return new ArrayList<>();
- }
- String searchIn = finder.getAnnotatedValue(UsingPaths.class, String.class, "searchIn");
- List<String> includes = finder.getAnnotatedValues(UsingPaths.class, String.class, "includes");
- List<String> excludes = finder.getAnnotatedValues(UsingPaths.class, String.class, "excludes");
- return storyFinder().findPaths(searchIn, includes, excludes);
- }
- @SuppressWarnings("unchecked")
- private StoryFinder storyFinder() {
- return instanceOf(StoryFinder.class,
- (Class<? extends StoryFinder>) finder.getAnnotatedValue(UsingPaths.class, Class.class, "storyFinder"));
- }
- private boolean control(AnnotationFinder finder, String name) {
- return finder.getAnnotatedValue(UsingEmbedder.class, Boolean.class, name);
- }
- private <T> T configurationElement(AnnotationFinder finder, String name, Class<T> type) {
- Class<T> implementation = elementImplementation(finder, name);
- return instanceOf(type, implementation);
- }
- @SuppressWarnings("unchecked")
- protected <T> Class<T> elementImplementation(AnnotationFinder finder, String name) {
- return finder.getAnnotatedValue(Configure.class, Class.class, name);
- }
- private Properties loadProperties(String systemPropertiesCSV) {
- Properties properties = new Properties();
- try {
- properties.load(new ByteArrayInputStream(systemPropertiesCSV.replace(",", "\n").getBytes()));
- } catch (IOException e) {
- // return empty map
- }
- return properties;
- }
- protected ParameterConverters parameterConverters(AnnotationFinder annotationFinder,
- ResourceLoader resourceLoader, TableTransformers tableTransformers) {
- ParameterConverters parameterConverters = new ParameterConverters(resourceLoader, tableTransformers);
- for (Class<ParameterConverter> converterClass : annotationFinder.getAnnotatedClasses(Configure.class,
- ParameterConverter.class, "parameterConverters")) {
- parameterConverters.addConverters(instanceOf(ParameterConverter.class, converterClass));
- }
- return parameterConverters;
- }
- protected <T, V extends T> T instanceOf(Class<T> type, Class<V> ofClass) {
- try {
- // by classloader constructor
- try {
- Constructor<V> constructor =
- ofClass.getConstructor(ClassLoader.class);
- return constructor.newInstance(annotatedClass.getClassLoader());
- } catch (NoSuchMethodException ns) {
- // fall-back to another approach of object construction
- }
- // by class constructor
- try {
- Constructor<V> constructor =
- ofClass.getConstructor(Class.class);
- return constructor.newInstance(annotatedClass);
- } catch (NoSuchMethodException ns) {
- // fall-back to another approach of object construction
- }
- // by class instance
- return ofClass.newInstance();
- } catch (Exception e) {
- annotationMonitor.elementCreationFailed(ofClass, e);
- throw new InstantiationFailed(ofClass, type, e);
- }
- }
- protected AnnotationMonitor annotationMonitor() {
- return annotationMonitor;
- }
- protected AnnotationFinder annotationFinder() {
- return finder;
- }
- public Object embeddableInstance() {
- return injectEmbedder(buildEmbedder(), annotatedClass);
- }
- protected Object injectEmbedder(Embedder embedder, Class<?> annotatedClass) {
- try {
- Object instance = annotatedClass.newInstance();
- if (instance instanceof Embeddable) {
- Embeddable embeddable = (Embeddable) instance;
- embeddable.useEmbedder(embedder);
- }
- if (instance instanceof ConfigurableEmbedder) {
- ConfigurableEmbedder configurableEmbedder = (ConfigurableEmbedder) instance;
- configurableEmbedder.useConfiguration(embedder.configuration());
- configurableEmbedder.useStepsFactory(embedder.stepsFactory());
- }
- return instance;
- } catch (Exception e) {
- annotationMonitor.elementCreationFailed(annotatedClass, e);
- throw new InstantiationFailed(annotatedClass, e);
- }
- }
- @SuppressWarnings("serial")
- public static class InstantiationFailed extends RuntimeException {
- public InstantiationFailed(Class<?> ofClass, Class<?> type, Throwable cause) {
- super("Failed to instantiate " + ofClass + " of type " + type, cause);
- }
- public InstantiationFailed(Class<?> ofClass, Throwable cause) {
- super("Failed to instantiate " + ofClass, cause);
- }
- }
- }