11 Replies Latest reply on Jul 31, 2013 10:04 PM by adrian.f.cole

    Google Guice Extension

    jmnarloch

      Some time ago I had this idea for creating a Google Guice extension as an alternative to CDI or Spring IOC. I didn't forgot about it but due to excess of activities it had to wait.

       

      Recently I had made major update and the results can be found here https://github.com/jmnarloch/arquillian-extension-guice

       

      Very briefly: The extension is quite similar to the Spring one. It alows for configuring Guice modules through annotation per each test case. Besides that it auto packages the Guice dependencies.

       

      Example of test case:

       

      {code}

      @RunWith(Arquillian.class)

      @GuiceConfiguration({EmployeeModule.class})

      public class DefaultEmployeeServiceTestCase {

       

          @Deployment

          public static JavaArchive createTestArchive() {

              return ShrinkWrap.create(JavaArchive.class, "guice-test.jar")

                      .addClasses(Employee.class,

                              EmployeeService.class, DefaultEmployeeService.class,

                              EmployeeRepository.class, DefaultEmployeeRepository.class,

                              EmployeeModule.class);

          }

       

          @Inject

          private EmployeeService employeeService;

       

          @Test

          public void testGetEmployees() {

              List<Employee> result = employeeService.getEmployees();

       

              assertNotNull("Method returned null list as result.", result);

              assertEquals("Two employees were expected.", 2, result.size());

          }

      }

       

      {code}

       

      So the question is do we need it and do we want it?

        • 1. Re: Google Guice Extension
          jmnarloch

          One major update on this issue:

           

          Since the Guice Injector is being configured based on the module instances instead of just "static" configuration classes like in Spring, so in some cases it would be preferable to instantiate the Injector with instantiated and fully configured modules.

           

          So I would like to propose an alternative way for enabling the Guice capabilitites on the test case, by giving the user complete control over how the Injector will be created. Example:

           

           

          {code}

          @RunWith(Arquillian.class)

          public class CustomInjectorTestCase {

           

              @Deployment

              public static JavaArchive createTestArchive() {

                  return ShrinkWrap.create(JavaArchive.class, "guice-test.jar")

                          .addClasses(Employee.class,

                                  EmployeeService.class, DefaultEmployeeService.class,

                                  EmployeeRepository.class, DefaultEmployeeRepository.class,

                                  EmployeeModule.class);

              }

           

              @GuiceInjector

              public static Injector createInjector() {

           

                  return Guice.createInjector(new EmployeeModule());

              }

           

              @Inject

              private EmployeeService employeeService;


              @Test

              public void testGetEmployees() {

           

                  List<Employee> result = employeeService.getEmployees();

           

                  assertNotNull("Method returned null list as result.", result);

                  assertEquals("Two employees were expected.", 2, result.size());

              }

          }

           

          {code}

           

          The above code looks pretty much similar with one major difference, the static createInjector method annotated with @GuiceInjector. This way user can have complete control how the Injector can be instantiated and the actuall type that will be used. One of potential usage is for example registering JpaPersistModle which takes as the argument name of the JPA persistence unit.

           

          {code}

                  return Guice.createInjector(new EmployeeModule(), new JpaPersistModle("jpa-test-unit"));

          {code}

          • 2. Re: Google Guice Extension
            jmnarloch

            I'm finally closing to finish this extension entirely. So within days, it could be finally released.

            • 3. Re: Google Guice Extension
              loic.lacombe

              Just so that you don't get desperate that people have no interest: your stuff looks really cool. It's kinda counterintuitive to use Guice and CDI at the same time, but CDI is so verbose that your lib comes as a god sent. I'm spending all my time saying "damn, it would be so easier to use plain Guice for that part".

              Thanks, I'll try and evaluate your extension.

               

              Sure it perverts the core principles of CDI, but I don't care as long as I can reduce the cost of using CDI

              • 4. Re: Google Guice Extension
                adrian.f.cole

                Can you explain how this could (or does) interact with the guice servlet ecosystem?

                 

                At Netflix, we use karyon, which is an extension of the guice servlet filter

                 

                ex.


                    <filter>

                        <filter-name>guiceFilter</filter-name>

                        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>

                    </filter>

                 

                    <filter-mapping>

                        <filter-name>guiceFilter</filter-name>

                        <url-pattern>/*</url-pattern>

                    </filter-mapping>

                 

                    <listener>

                        <listener-class>com.netflix.karyon.server.guice.KaryonGuiceContextListener</listener-class>

                    </listener>

                 

                I'm hoping to be able to write a test that can inject something bound by one of these injectors.  Any ideas of how this could interact with the guice extension?

                 

                -A

                • 5. Re: Google Guice Extension
                  jmnarloch

                  Hi Adrian,

                   

                  At the moment we don't have such support in the Guice extension. Although I already done similar thing in the Spring extension, so this is definetly doable. In Guice the only difference would be the fact that this would be more "invasive" and would require some way to capture the injector and bind it with the executing thread, the same injector can be accessed from the test afterwards. In Spring this is provieded out of the box through WebApplicationContextUtils. I could try to develop such functionality.

                  • 6. Re: Google Guice Extension
                    jmnarloch

                    I had quick look at the Guice Servlet source code and I think we can go with two different aproaches:

                     

                    • Extension ServletContextListener

                     

                    This can be done on two ways.

                    In the first approach we can introduce class that should replace the the GuiceServletContextListener and should be used instead of it. Although this can won't be usefull with the existing code and won't be usefull with integrating with extension that are based on the Guice Servlet module.

                     

                    Example:

                     

                     

                    {code}

                    public class TestGuiceServletConfig extends ArquillianGuiceServletContextListener {

                      @Override

                      protected Injector getInjector() {

                         ...

                      }

                    }

                    {code}

                     

                    web.xml

                     

                    <listener>

                      <listener-class>com.example.test.TestGuiceServletConfig</listener-class>
                    </listener>

                     

                    An alternative approach would be relay on the order of the executing listeners and add listener that would "capture" the injector created by the oder context listeners. The drowback would be that it would have to declared as a last listener in the web.xml, so this might be a bit error prone solution.

                     

                    Example:

                     

                    <listener>

                       <!-- Application injector configuration -->
                     
                    <listener-class>com.example.test.GuiceServletConfig</listener-class>
                    </listener>

                     

                    <listener>

                       <!-- Test injector configuration -->
                     
                    <listener-class>org.jboss.arquillian.guice.servlet.ArquilllianGuiceServletContextListener</listener-class>
                    </listener>

                     

                    • The second possible approach would be to introdce Arquillian Guice filter, that should be used instead of the standard one.

                     

                    For instance:

                     

                      <filter>

                        <filter-name>arquillianGuiceFilter</filter-name>
                       
                    <filter-class>org.jboss.arquillian.guice.servlet.ArquiilianGuiceFilter</filter-class>
                     
                    </filter>

                     
                    <filter-mapping>
                       
                    <filter-name>arquillianGuiceFilter</filter-name>

                        <url-pattern>/*</url-pattern>
                     
                    </filter-mapping>

                     

                     

                    What do you think? Which solution would best to implement?

                    • 7. Re: Google Guice Extension
                      adrian.f.cole

                      +1 to ArquiilianGuiceFilter


                      It is a simple string replacement on web.xml, and could be automated in shrinkwrap or our use of it.

                       

                      Thanks for having a look!

                      • 8. Re: Google Guice Extension
                        jmnarloch

                        Hi again,

                         

                        I have prototyped the implementation. Now I'm going to polish it and I think that later this week I can push that to the repository.

                        • 9. Re: Google Guice Extension
                          jmnarloch

                          What do you think about something like this: https://github.com/arquillian/arquillian-extension-guice/pull/2/files#L18R47?

                           

                          Basicly all you will have to do is to define the ArquillianGuiceFilter in your web.xml, like:

                           

                              <filter>

                                  <filter-name>guiceFilter</filter-name>

                                  <filter-class>org.jboss.arquillian.guice.api.servlet.ArquillianGuiceFilter</filter-class>

                              </filter>

                           

                           

                              <filter-mapping>

                                  <filter-name>guiceFilter</filter-name>

                                  <url-pattern>/*</url-pattern>

                              </filter-mapping>

                           

                           

                          And afterards annotated your test with @GuiceWebConfiguration and that's it, you can automaticly inject any dependency that has been created within your servlet container. So you should be able to inject servlets, filters and any other Guice configured instances.

                           

                          Example code:

                           

                           

                          {code}

                           

                          @GuiceWebConfiguration

                          @RunWith(Arquillian.class)

                          public class WebInjectorTestCase {

                           

                           

                              /**

                               * Creates the test deployment.

                               *

                               * @return the test deployment

                               */

                              @Deployment

                              public static Archive createTestArchive() {

                                  return ShrinkWrap.create(WebArchive.class, "guice-test.war")

                                          .addClasses(Employee.class,

                                                  EmployeeService.class, DefaultEmployeeService.class,

                                                  EmployeeRepository.class, DefaultEmployeeRepository.class,

                                                  EmployeeModule.class, EmployeeModuleContextListener.class)

                                          .addAsWebInfResource("WEB-INF/web.xml", "web.xml");

                              }

                           

                           

                              /**

                               * <p>The injected {@link EmployeeService}.</p>

                               */

                              @Inject

                              private EmployeeService employeeService;

                           

                           

                              /**

                               * <p>Tests the {@link EmployeeService#getEmployees()}</p>

                               */

                              @Test

                              public void testGetEmployees() {

                           

                           

                                  List<Employee> result = employeeService.getEmployees();

                           

                           

                                  assertNotNull("Method returned null list as result.", result);

                                  assertEquals("Two employees were expected.", 2, result.size());

                              }

                          }

                          {code}

                           

                          I will ask for review and I think that after this change goes to master we could have Alpha 2 release. Stricly speaking there is no road map for the Guice Extension, but in the exchange we can have pretty often releases

                          • 10. Re: Google Guice Extension
                            adrian.f.cole

                            Good job.  I can test this (in karyon) as soon as you tell me the release is out.

                            • 11. Re: Google Guice Extension
                              adrian.f.cole

                              I'm having build infra issues testing the snapshot which aren't likely to resolve in the next day or two.  Please release another alpha (beta etc), and I'll give it a go!