SpringStepsFactory.java

package org.jbehave.core.steps.spring;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.steps.AbstractStepsFactory;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ClassUtils;

/**
 * An {@link InjectableStepsFactory} that uses Spring's
 * {@link ApplicationContext} for the composition and instantiation of all
 * components that contain JBehave annotated methods.
 * 
 * @author Paul Hammant
 * @author Mauro Talevi
 */
public class SpringStepsFactory extends AbstractStepsFactory {

    private final ApplicationContext context;

    public SpringStepsFactory(Configuration configuration,
            ApplicationContext context) {
        super(configuration);
        this.context = context;
    }

    @Override
    protected List<Class<?>> stepsTypes() {
        // Using set because in some cases (eg: scoped proxies),
        // there may be two actual beans for each defined bean).
        Set<Class<?>> types = new HashSet<>();
        for (String name : context.getBeanDefinitionNames()) {
            Class<?> type = beanType(name);
            if (isAllowed(type) && hasAnnotatedMethods(type)) {
                types.add(type);
            }
        }
        return new ArrayList<>(types);
    }

    /**
     * Checks if type returned from context is allowed, i.e. not null and not
     * abstract.
     *
     * @param type
     *            the Class of the bean
     * @return A boolean, <code>true</code> if allowed
     */
    protected boolean isAllowed(Class<?> type) {
        return type != null && !Modifier.isAbstract(type.getModifiers());
    }

    @Override
    public Object createInstanceOfType(Class<?> type) {
        for (String name : context.getBeanDefinitionNames()) {
            Class<?> beanType = beanType(name);
            if (type.equals(beanType)) {
                return context.getBean(name);
            }
        }
        throw new StepsInstanceNotFound(type, this);
    }

    /**
     * Return the bean type, untangling the proxy if needed
     *
     * @param name
     *            the bean name
     * @return The Class of the bean
     */
    private Class<?> beanType(String name) {
        Class<?> type = context.getType(name);
        if (ClassUtils.isCglibProxyClass(type)) {
            return AopProxyUtils.ultimateTargetClass(context.getBean(name));
        }
        return type;
    }

}