13

How do I allow CDI injection of resources into restful web service resources? I am running on standard java using weld 2 (cdi), jersey (jaxrs), and grizzly (web server). Here is my simple web resource:

import training.student.StudentRepository;
import javax.inject.Inject;
import javax.ws.rs.*;

@Path("student")
public class StudentWebResource {
  @Inject
  private StudentRepository studentRepository;  

  @GET
  @Path("count")
  @Produces(MediaType.TEXT_PLAIN)
  public Integer getCount() {
    return studentRepository.studentCount();
  }
}

And here is how I've got weld starting my simple web server:

public class Main {
  public static void main(String[] args) throws Exception {
    startCdiApplication();
  }

  public static void startCdiApplication() throws Exception {
    Weld weld = new Weld();
    try {
      WeldContainer container = weld.initialize();
      Application application = container.instance().select(WebServer.class).get();
      application.run();
    } 
    finally {
      weld.shutdown();
    }
  }
}

And the code that I suspect will need to be modified to inform jersey to use weld for CDI inject resolution:

...
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

public class WebServer implements Application {

  /*
   * startup the grizzly http server to make available the restful web services
   */
  private void startWebServer() throws IOException, InterruptedException {
    final ResourceConfig resourceConfig = new ResourceConfig().packages("training.webservice").register(new JacksonFeature());
    final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(getBaseUri(), resourceConfig);
    server.start();
    Thread.currentThread().join();
  }

  ...

  @Override
  public void run() throws IOException, InterruptedException {
    startWebServer();
  }
}
Brice Roncace
  • 10,110
  • 9
  • 60
  • 69

3 Answers3

13

After seeing this stackoverflow post, I implemented the following solution. Not sure if it is the best route to take, but it worked.

I created an hk2 Binder and registered the Binder:

public class WebServiceBinder extends AbstractBinder {

  @Override
  protected void configure() {
    BeanManager bm = getBeanManager();
    bind(getBean(bm, StudentRepository.class))
        .to(StudentRepository.class);
  }

  private BeanManager getBeanManager() {
    // is there a better way to get the bean manager?
    return new Weld().getBeanManager();
  }

  private <T> T getBean(BeanManager bm, Class<T> clazz) {
    Bean<T> bean = (Bean<T>) bm.getBeans(clazz).iterator().next();
    CreationalContext<T> ctx = bm.createCreationalContext(bean);
    return (T) bm.getReference(bean, clazz, ctx); 
  }
}

Then modified the ResourceConfig instantiation from above to:

final ResourceConfig resourceConfig = new ResourceConfig()
    .packages("training.webservice")
    .register(new JacksonFeature())
    .register(new WebServiceBinder());
Community
  • 1
  • 1
Brice Roncace
  • 10,110
  • 9
  • 60
  • 69
  • That will work, but you're better off to use the SE integration of the CDI implementation you're using (it's not in the spec, but they all do it). – LightGuard Jun 21 '13 at 16:36
  • Do you mean instead of initializing CDI via the Weld object I should be using (in the case of Weld) the StartMain.main static method and then have a `public void main(@Observes ContainerInitialized event)`? or are you referring to the hk2 CDI implementation? – Brice Roncace Jun 21 '13 at 20:34
  • Either one will work. If you're in an OSGI environment and need the separation the hk2 or weld-osgi project would work. – LightGuard Jun 21 '13 at 20:52
  • Could you make it work using the ContainerInitialized approach? – javabeats Sep 23 '13 at 15:20
  • 3
    BTW, a better way to obtain the BeanManager is CDI.current().getBeanManager(). – javabeats Sep 23 '13 at 18:45
  • @javabeats Thanks for the information... and no, I didn't end up observing the `ContainerInitialized` event. – Brice Roncace Sep 23 '13 at 22:54
  • I used your approach too (save for the part where we get the BeanManager reference), and it worked. However, like you, I feel this is not optimal. I searched everywhere, and I couldn't find a solution to integrate Grizzly's H2K with a CDI implementation. Too bad. – javabeats Sep 24 '13 at 14:57
  • OMG, you guys saved my life. Using the bean manager for the injection binding is the only way I got my @ApplicationScoped CDI beans (Weld 2.0.4) injected in my JAX-RS resource class (Jersey 2.3.1). But I wonder why it is so complicated getting CDI and JAX-RS work together in a servlet container? – Sebastian S. Sep 27 '13 at 23:49
  • 1
    Warning. This answer is outdated since Jersey 2. – G. Demecki Jul 31 '15 at 14:49
6

The selected answer dates from a while back. It is not practical to declare every binding in a custom HK2 binder. I just had to add one dependency. Even though it was designed for Glassfish it fits perfectly into other containers. I'm using Tomcat / Grizzly.

   <dependency>
        <groupId>org.glassfish.jersey.containers.glassfish</groupId>
        <artifactId>jersey-gf-cdi</artifactId>
        <version>2.14</version>
    </dependency>

Here is an example with JerseyTest (same principle if you run it from a main method). I just had to declare a dependency on weld-se and declare a Weld container before instantiating my resources - as you also did - and it works out of the box.

public class GrizzlyTest extends JerseyTest {
    private Weld weld;
    private WeldContainer container;

    @Override
    protected Application configure() {
        weld = new Weld();
        container = weld.initialize();
        return new ResourceConfig(MyResource.class);
    }

    @Test
    public void test() {
        System.out.println(target("myresource").request().get(String.class));
    }

    @After
    public void after() {
        weld.shutdown();
    }
}
Olivier Tonglet
  • 3,312
  • 24
  • 40
  • True that accepted answer is outdated since when Jersey 2 has seen the daylight but ... your code snippet is also flawed: `new Weld()` should be used to start Weld container in a standalone env, not inside the servlet container. Register Weld servlet listener instead. – G. Demecki Jul 31 '15 at 14:43
  • Could you then post a working example registering a Weld servlet listener on a JerseyTest config ? – Olivier Tonglet Jul 31 '15 at 15:02
  • Don't get me wrong, your code snippet is perfectly ok, as it is just a test class. But OP asked about enabling CDI (Weld) in a servlet container. – G. Demecki Aug 03 '15 at 10:36
2

Since at least Weld 2.2.0.Final there is no need to mess up with HK2 Binder.

As official Weld documentation states you just need to register org.jboss.weld.environment.servlet.Listener. Code snipped from doc:

public class Main {
    public static void main(String[] args) throws ServletException, LifecycleException {
        Tomcat tomcat = new Tomcat();
        Context ctx = tomcat.addContext("/", new File("src/main/resources").getAbsolutePath());

        Tomcat.addServlet(ctx, "hello", HelloWorldServlet.class.getName());
        ctx.addServletMapping("/*", "hello");

        ctx.addApplicationListener(Listener.class.getName());

        tomcat.start();
        tomcat.getServer().await();
    }

    public static class HelloWorldServlet extends HttpServlet {
        @Inject
        private BeanManager manager;

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/plain");
            resp.getWriter().append("Hello from " + manager);
        }
    }
}

Above servlet listener manages the whole lifecycle of the Weld container. So there is no need to:

 Weld weld = new Weld();
 WeldContainer container = weld.initialize();

UPDATE As @EdMelo pointed out, Grizzly HTTP server is not a fully compliant Servlet container. I didn't know this, thanks for this hint. So I'm not sure, if my answer still applies here.

G. Demecki
  • 10,145
  • 3
  • 58
  • 58