GuiceAnnotationBuilder.java

  1. package org.jbehave.core.configuration.guice;

  2. import java.util.ArrayList;
  3. import java.util.List;

  4. import com.google.inject.AbstractModule;
  5. import com.google.inject.Binding;
  6. import com.google.inject.Guice;
  7. import com.google.inject.Injector;
  8. import com.google.inject.Key;
  9. import com.google.inject.Module;
  10. import com.google.inject.TypeLiteral;
  11. import com.google.inject.util.Modules;

  12. import org.jbehave.core.annotations.guice.UsingGuice;
  13. import org.jbehave.core.configuration.AnnotationBuilder;
  14. import org.jbehave.core.configuration.AnnotationFinder;
  15. import org.jbehave.core.configuration.AnnotationMonitor;
  16. import org.jbehave.core.configuration.AnnotationRequired;
  17. import org.jbehave.core.configuration.Configuration;
  18. import org.jbehave.core.configuration.PrintStreamAnnotationMonitor;
  19. import org.jbehave.core.io.ResourceLoader;
  20. import org.jbehave.core.model.TableTransformers;
  21. import org.jbehave.core.steps.CompositeStepsFactory;
  22. import org.jbehave.core.steps.InjectableStepsFactory;
  23. import org.jbehave.core.steps.ParameterConverters;
  24. import org.jbehave.core.steps.ParameterConverters.ParameterConverter;
  25. import org.jbehave.core.steps.guice.GuiceStepsFactory;

  26. /**
  27.  * Extends {@link AnnotationBuilder} to provide Guice-based dependency injection
  28.  * if {@link UsingGuice} annotation is present.
  29.  *
  30.  * @author Cristiano GaviĆ£o
  31.  * @author Mauro Talevi
  32.  */
  33. public class GuiceAnnotationBuilder extends AnnotationBuilder {

  34.     public Injector injector;

  35.     public GuiceAnnotationBuilder(Class<?> annotatedClass) {
  36.         this(annotatedClass, new PrintStreamAnnotationMonitor());
  37.     }

  38.     public GuiceAnnotationBuilder(Class<?> annotatedClass, AnnotationMonitor annotationMonitor) {
  39.         super(annotatedClass, annotationMonitor);
  40.     }

  41.     @Override
  42.     public Configuration buildConfiguration() throws AnnotationRequired {

  43.         AnnotationFinder finder = annotationFinder();
  44.         if (finder.isAnnotationPresent(UsingGuice.class)) {
  45.             @SuppressWarnings("rawtypes")
  46.             List<Class> moduleClasses = finder.getAnnotatedValues(UsingGuice.class, Class.class, "modules");
  47.             List<Module> modules = new ArrayList<>();
  48.             for (Class<Module> moduleClass : moduleClasses) {
  49.                 try {
  50.                     modules.add(moduleClass.newInstance());
  51.                 } catch (Exception e) {
  52.                     annotationMonitor().elementCreationFailed(moduleClass, e);
  53.                 }
  54.             }
  55.             // creating injector with any modules found
  56.             if (modules.size() > 0) {
  57.                 injector = createInjector(modules);
  58.             }
  59.         } else {
  60.             annotationMonitor().annotationNotFound(UsingGuice.class, annotatedClass());
  61.         }
  62.         return super.buildConfiguration();
  63.     }

  64.     @Override
  65.     public InjectableStepsFactory buildStepsFactory(Configuration configuration) {
  66.         InjectableStepsFactory factoryUsingSteps = super.buildStepsFactory(configuration);
  67.         if (injector != null) {
  68.             return new CompositeStepsFactory(new GuiceStepsFactory(configuration, injector), factoryUsingSteps);
  69.         }
  70.         return factoryUsingSteps;
  71.     }

  72.     @Override
  73.     protected ParameterConverters parameterConverters(AnnotationFinder annotationFinder, ResourceLoader resourceLoader,
  74.             TableTransformers tableTransformers) {
  75.         ParameterConverters converters = super.parameterConverters(annotationFinder, resourceLoader, tableTransformers);
  76.         if (injector != null) {
  77.             return converters.addConverters(findConverters(injector));
  78.         }
  79.         return converters;
  80.     }

  81.     /**
  82.      * Finds any {@link ParameterConverter} defined in the given injector and,
  83.      * if none found, recurses to its parent.
  84.      *
  85.      * @param injector
  86.      *            the Injector
  87.      * @return A List of ParameterConverter instances
  88.      */
  89.     private List<ParameterConverter> findConverters(Injector injector) {
  90.         List<Binding<ParameterConverter>> bindingsByType = injector
  91.                 .findBindingsByType(new TypeLiteral<ParameterConverter>() {
  92.                 });
  93.         if (bindingsByType.isEmpty() && injector.getParent() != null) {
  94.             return findConverters(injector.getParent());
  95.         }
  96.         List<ParameterConverter> converters = new ArrayList<>();
  97.         for (Binding<ParameterConverter> binding : bindingsByType) {
  98.             converters.add(binding.getProvider().get());
  99.         }
  100.         return converters;
  101.     }

  102.     @Override
  103.     protected <T, V extends T> T instanceOf(final Class<T> type, final Class<V> ofClass) {
  104.         if (injector != null) {
  105.             if (!type.equals(Object.class)) {
  106.                 try {
  107.                     boolean bindingFound = findBinding(injector, type);
  108.                     if (bindingFound) {
  109.                         // when binding found, just get the instance associated
  110.                         return injector.getInstance(type);
  111.                     } else {
  112.                         // when binding not found, need to explicitly bind type
  113.                         // + ofClass
  114.                         Module module = new AbstractModule() {

  115.                             @Override
  116.                             protected void configure() {
  117.                                 if (!type.equals(ofClass)) {
  118.                                     bind(type).to(ofClass);
  119.                                 } else {
  120.                                     // when type and oFClass are
  121.                                     // binding the ofClass
  122.                                     bind(ofClass);
  123.                                 }
  124.                             }
  125.                         };

  126.                         injector = injector.createChildInjector(module);
  127.                         return injector.getInstance(type);
  128.                     }
  129.                 } catch (Exception e) {
  130.                     // fall back on getting instance ofClass
  131.                     return injector.getInstance(ofClass);
  132.                 }
  133.             } else {
  134.                 return injector.getBinding(ofClass).getProvider().get();
  135.             }
  136.         }
  137.         return super.instanceOf(type, ofClass);
  138.     }

  139.     /**
  140.      * Finds binding for a type in the given injector and, if not found,
  141.      * recurses to its parent
  142.      *
  143.      * @param injector
  144.      *            the current Injector
  145.      * @param type
  146.      *            the Class representing the type
  147.      * @return A boolean flag, <code>true</code> if binding found
  148.      */
  149.     private boolean findBinding(Injector injector, Class<?> type) {
  150.         boolean found = false;
  151.         for (Key<?> key : injector.getBindings().keySet()) {
  152.             if (key.getTypeLiteral().getRawType().equals(type)) {
  153.                 found = true;
  154.                 break;
  155.             }
  156.         }
  157.         if (!found && injector.getParent() != null) {
  158.             return findBinding(injector.getParent(), type);
  159.         }

  160.         return found;
  161.     }

  162.     protected Injector createInjector(List<Module> modules) {
  163.         if (injector != null) {
  164.             return injector;
  165.         }
  166.         Injector root = Guice.createInjector(new AbstractModule() {        
  167.             @Override
  168.             protected void configure() {
  169.        
  170.             }
  171.         });
  172.         return root.createChildInjector(Modules.combine(modules));
  173.     }

  174.     protected Injector injector() {
  175.         return injector;
  176.     }
  177. }