Search code examples
cdijava-ee-7websphere-liberty

Java EE Dependency Injection in Websphere Liberty profile


I am trying to use CDI in my very simple web app that runs in Websphere Liberty profile installed via Docker.

However the injection fails unless I specify a scope annotation (e.g. @ApplicationScoped) on the injected bean, though according to a lot of online tutorials (e.g. this), Java EE specs do not require this.

Below is the code that fails:

HelloWorldServlet.java

package my.simple.app;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {

    static String PAGE_HEADER = "<html><head /><body>";
    static String PAGE_FOOTER = "</body></html>";

    @Inject
    HelloService helloService;

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.println(PAGE_HEADER);
        writer.println("<h1>" + helloService.createHelloMessage("World") + "</h1>");
        writer.println(PAGE_FOOTER);
        writer.close();
    }

}

HelloService.java

package my.simple.app;

public class HelloService {
    String createHelloMessage(String name) {
        return "Hello " + name + "!";
    }
}

server.xml (Docker image is websphere-liberty:javaee7)

<server description="default servlet engine">

    <httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443" />

    <!-- Enable features -->
    <featureManager>
        <feature>servlet-3.1</feature>
        <feature>cdi-1.2</feature>
    </featureManager>

</server>

However I get this error

Error 404: javax.servlet.UnavailableException: SRVE0319E: For the [my.simple.app.HelloWorldServlet] servlet, my.simple.app.HelloWorldServlet servlet class was found, but a resource injection failure has occurred. The @Inject java.lang.reflect.Field.helloService reference of type my.simple.app.HelloService for the null component in the app.war module of the app application cannot be resolved.

However once I add @ApplicationScoped to HelloService it all starts working.

What I am doing wrong?

Solution:

In CDI1.2 (which I am using) by default only annotated beans are discovered. To make all beans be discovered, and explicit discovery mode needs to be enabled in beans.xml

Links:


Solution

  • You can force CDI to treat the servlet as a bean and perform injection by changing the bean discovery mode to all.

    This article provides some useful background and an example of this:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
       http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="all">
    </beans>
    

    Or, in WDT, you can generate this by right-clicking the project and selecting Java EE Tools -> Generate CDI Beans Deployment Descriptor Stub, and be sure to select all from the drop-down "Bean discovery mode" selection.

    The downside is the performance hit since the app will take longer to start up, but that's a tradeoff you could make to avoid recompiling.