Recently I tried Tomcat 10.0.10 and when trying to inject the connection pool as a JNDI resource find out that the @Resource
annotation doesn't work.
Then I tried obtain it programmatically by creating a InitialContext
and it worked. Initially I thought it was only for the java:comp/env/jdbc so I tried with a simple bean like below and tried to inject it with the @Resource
annotation it didn't work again. When I try to obtain it programmatically by creating a InitialContext
and it works. Then I check whether the @PostConstruct
or @PreDestroy
annotation works and found out that they also don't work.
package lk.ijse.test.tomcatdbcp;
public class Something {
}
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="bean/Something" auth="Container"
type="lk.ijse.test.tomcatdbcp.Something"
factory="org.apache.naming.factory.BeanFactory"
/>
</Context>
<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="false" xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<resource-env-ref>
<resource-env-ref-name>bean/Something</resource-env-ref-name>
<resource-env-ref-type>lk.ijse.test.tomcatdbcp.Something</resource-env-ref-type>
</resource-env-ref>
</web-app>
package lk.ijse.test.tomcatdbcp;
import java.io.*;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import javax.naming.InitialContext;
import javax.naming.NamingException;
@WebServlet(name = "helloServlet", value = "/hello", loadOnStartup = 1)
public class HelloServlet extends HttpServlet {
private String message;
@Resource(name= "java:comp/env/bean/Something")
private Something something;
@PostConstruct
public void doSomething(){
System.out.println("Does it work?");
}
public void init() {
message = "Hello World!";
try {
InitialContext ctx = new InitialContext();
Something lookup = (Something) ctx.lookup("java:comp/env/bean/Something");
System.out.println(lookup);
System.out.println(something); // null
} catch (NamingException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
// Hello
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" message "</h1>");
out.println("</body></html>");
}
public void destroy() {
}
}
To reproduce the same issue, I created a sample repo here: https://github.com/sura-boy-playground/play-with-tomcat10 (Complete code can be found there)
At first, I had used javax.annotation.Resource
annotation, so I thought that was the reason because of the javax.*
to jakarta.*
namespace change. Then I tried it with jakarta.annotation.Resource
but the result was same.
I tried the same application with Tomcat 9.0.41 plus javax.*
namespace, it works perfectly.
Is there any extra stuff that I need to do on Tomcat 10.0.10 to enable these annotations? I dug the Tomcat 10 documentation but I wasn't able to find out any thing related to my issue.
I found out that there was a similar case in Tomcat 7 previously, but I don't like that kind of workaround now. Tomcat @Resource annotations API annotation stops working in Tomcat 7
CodePudding user response:
You should declare the scope of your jakarta.annotation
dependency as provided
:
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
If you have two copies of jakarta.annotation.Resource
(one in the common classloader and one in your application's classloader), the two classes are different. The InstanceManager
will look for fields annotated with the common classloader's copy of @Resource
, while the something
field is annotated with your webapp's copy of @Resource
.
While adding jakarta.annotation
with a scope different from provided
is certainly a mistake, it is probably a common mistake and Tomcat has a fail-safe feature that ignores certain classes distributed with your application (cf. source code). Since jakarta.annotation.*
classes are not among them, you can file a (wishlist) bug report.
Remark: You will have the same problem in Tomcat 9.0 if you use Java 11 or later. Before Java 11 the javax.annotation.*
classes where included in the JRE. Servlet containers are required to look in the bootstrap/JRE classloader before looking in the webapp classloader (overriding javax.*
classes is a breach of Java's licence), therefore Tomcat would never find the additional copy of the classes.