Reputation: 960
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:
Upvotes: 2
Views: 2841
Reputation: 161
Although what Scott suggests fixes your problem, here is the full picture about how it works.
In CDI 1.2, all applications are CDI enabled by default.
If there is an empty beans.xml or a beans.xml with the bean-discovery-mode="all", all classes are opted in for beans.
In the absence of the beans.xml or beans.xml with bean-discovery-mode="annotated", every class will be scanned for beans. Only the classes annotated with bean defining annotations (https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_defining_annotations) are treated as beans.
In your first example, since you don't have beans.xml, the scanning for bean-defining annotations was performed. As no bean defining annotation was found, cdi was disabled. As a consequence, the injection failed.
To enable the injection, there are two solutions: 1. Annotate the class HelloService with a bean defining annotations (e.g. any scopes: ApplicationScoped, RequestScoped, SessionScoped, ConversationScoped, Dependent etc). This will make HelloService a CDI bean and then Injection will succeed. 2. Add an empty beans.xml or beans.xml with beans-discovery-mode="all" under WEB-INF for .war files or META-INF for .jar files
For more information about bean archives, please refer to https://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#bean_archive.
Upvotes: 3
Reputation: 5320
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.
Upvotes: 5
Reputation: 316
I'm not familiar enough with EJB, but you could try to add @Stateless or @Service annotation to your Service class
package my.simple.app;
@Stateless
//@Service
public class HelloService {
String createHelloMessage(String name) {
return "Hello " + name + "!";
}
}
Edit: Or if you can't modify HelloService as you mentioned after you could use Producer Method http://docs.oracle.com/javaee/6/tutorial/doc/gjdid.html
Upvotes: 0